├── .gitignore
├── README.md
├── appveyor.yml
├── assets
└── Screenshot.png
└── src
├── PowerCmd.Installer
├── Generated.wxs
├── PowerCmd.Installer.wixproj
└── Product.wxs
├── PowerCmd.sln
└── PowerCmd
├── App.config
├── App.xaml
├── App.xaml.cs
├── Communication
├── CmdProcessController.cs
├── NativeConsoleUtilities.cs
└── UiCmdProcessController.cs
├── Converters
├── ColorToBrushConverter.cs
└── ErrorBrushConverter.cs
├── Extensions
├── Highlighting
│ └── PathColorizer.cs
├── OutputAnalyzers
│ ├── DirCommandAnalyzer.cs
│ ├── IOutputAnalyzer.cs
│ ├── MainBrainShowUpdatesCommandAnalyzer.cs
│ └── MsBuildCommandAnalyzer.cs
└── SuggestionProviders
│ ├── CdHistoryProvider.cs
│ ├── DefaultSuggestionProvider.cs
│ └── ISuggestionProvider.cs
├── Models
├── CommandButton.cs
├── CommandExecutionInfo.cs
└── CommandExecutionResult.cs
├── PowerCmd.csproj
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── Themes
├── LeftMarginMultiplierConverter.cs
├── Styles.xaml
└── TreeViewItemExtensions.cs
├── ViewModels
└── MainWindowModel.cs
├── Views
├── ComboBoxUtilities.cs
├── MainWindow.xaml
└── MainWindow.xaml.cs
└── packages.config
/.gitignore:
--------------------------------------------------------------------------------
1 | src/**/bin/**
2 | src/**/obj/**
3 | src/packages/**
4 |
5 | **.suo
6 | **.user
7 | **.DotSettings
8 |
9 | **.sln.ide/**
10 | **.vs/**
11 |
12 | [Bb]in/
13 | [Oo]bj/
14 | *.nugetreferenceswitcher
15 | /src/.vs/config
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PowerCmd
2 |
3 | [](https://ci.appveyor.com/project/rsuter/powercmd)
4 |
5 | PowerCmd is a replacement for the native Windows Prompt with enhanced performance and additional features. The tool greatly improves script execution performance with optimized output rendering and adds features like output analysis, command buttons, improved suggestions and project directories.
6 |
7 | #### [Download latest PowerCmd MSI installer](http://rsuter.com/Projects/PowerCmd/installer.php)
8 |
9 | [Download latest **Build Artifacts** from AppVeyor](https://ci.appveyor.com/project/rsuter/powercmd/build/artifacts)
10 |
11 | 
12 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: '{build}'
2 | configuration: Release
3 | platform: Any CPU
4 | before_build:
5 | - cmd: nuget restore src/PowerCmd.sln
6 | build:
7 | verbosity: minimal
8 | artifacts:
9 | - path: src\PowerCmd\bin\Release
10 | name: PowerCmd
11 | - path: src\PowerCmd\Properties\AssemblyInfo.cs
12 | - path: src\PowerCmd.Installer\bin\Release\PowerCmd.msi
13 | name: PowerCmd.msi
--------------------------------------------------------------------------------
/assets/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RicoSuter/PowerCmd/cd966ba825f0701d943bdfa08305f31f7627dc00/assets/Screenshot.png
--------------------------------------------------------------------------------
/src/PowerCmd.Installer/Generated.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
--------------------------------------------------------------------------------
/src/PowerCmd.Installer/PowerCmd.Installer.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 3.10
7 | 666e63f1-ab82-4fdf-bb9d-d66e8c2991ca
8 | 2.0
9 | PowerCmd
10 | Package
11 | $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets
12 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets
13 |
14 |
15 | bin\$(Configuration)\
16 | obj\$(Configuration)\
17 | Debug
18 |
19 |
20 | bin\$(Configuration)\
21 | obj\$(Configuration)\
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | $(WixExtDir)\WixUtilExtension.dll
30 | WixUtilExtension
31 |
32 |
33 | $(WixExtDir)\WixUIExtension.dll
34 | WixUIExtension
35 |
36 |
37 |
38 |
39 | PowerCmd
40 | {8f22fa02-4448-426e-9cd2-a6e154963ad5}
41 | True
42 | True
43 | Binaries;Content;Satellites
44 | INSTALLFOLDER
45 |
46 |
47 |
48 |
49 |
50 | SourcePath=..\PowerCmd\bin\$(Configuration)
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/PowerCmd.Installer/Product.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 1
24 | 1
25 | WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 0
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
72 |
73 |
78 |
79 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/PowerCmd.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.24720.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerCmd", "PowerCmd\PowerCmd.csproj", "{8F22FA02-4448-426E-9CD2-A6E154963AD5}"
7 | EndProject
8 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerCmd.Installer", "PowerCmd.Installer\PowerCmd.Installer.wixproj", "{666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Debug|x86 = Debug|x86
14 | Release|Any CPU = Release|Any CPU
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}.Debug|x86.ActiveCfg = Debug|Any CPU
21 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}.Debug|x86.Build.0 = Debug|Any CPU
22 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}.Release|x86.ActiveCfg = Release|Any CPU
25 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}.Release|x86.Build.0 = Release|Any CPU
26 | {666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}.Debug|Any CPU.ActiveCfg = Debug|x86
27 | {666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}.Debug|Any CPU.Build.0 = Debug|x86
28 | {666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}.Debug|x86.ActiveCfg = Debug|x86
29 | {666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}.Debug|x86.Build.0 = Debug|x86
30 | {666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}.Release|Any CPU.ActiveCfg = Release|x86
31 | {666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}.Release|Any CPU.Build.0 = Release|x86
32 | {666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}.Release|x86.ActiveCfg = Release|x86
33 | {666E63F1-AB82-4FDF-BB9D-D66E8C2991CA}.Release|x86.Build.0 = Release|x86
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | EndGlobal
39 |
--------------------------------------------------------------------------------
/src/PowerCmd/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/PowerCmd/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/PowerCmd/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using Microsoft.ApplicationInsights;
4 | using MyToolkit.Storage;
5 |
6 | namespace PowerCmd
7 | {
8 | public partial class App : Application
9 | {
10 | public static TelemetryClient Telemetry = new TelemetryClient();
11 |
12 | public App()
13 | {
14 | InitializeTelemetry();
15 | }
16 |
17 | protected override void OnStartup(StartupEventArgs e)
18 | {
19 | Telemetry.TrackEvent("ApplicationStart");
20 | AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
21 | }
22 |
23 | protected override void OnExit(ExitEventArgs e)
24 | {
25 | Telemetry.TrackEvent("ApplicationExit");
26 | Telemetry.Flush();
27 | }
28 |
29 | private void InitializeTelemetry()
30 | {
31 | #if !DEBUG
32 | Telemetry.InstrumentationKey = "07e31099-575b-4a97-92b9-4e8f809c4d8f";
33 | Telemetry.Context.User.Id = ApplicationSettings.GetSetting("TelemetryUserId", Guid.NewGuid().ToString());
34 | Telemetry.Context.Session.Id = Guid.NewGuid().ToString();
35 | Telemetry.Context.Device.OperatingSystem = Environment.OSVersion.ToString();
36 | Telemetry.Context.Component.Version = GetType().Assembly.GetName().Version.ToString();
37 |
38 | ApplicationSettings.SetSetting("TelemetryUserId", Telemetry.Context.User.Id);
39 | #endif
40 | }
41 |
42 | private void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
43 | {
44 | Telemetry.TrackException(args.ExceptionObject as Exception);
45 | Telemetry.Flush();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/PowerCmd/Communication/CmdProcessController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Text;
4 | using System.Threading;
5 | using MyToolkit.Mvvm;
6 |
7 | namespace PowerCmd.Communication
8 | {
9 | public abstract class CmdProcessController
10 | {
11 | private readonly StringBuilder _output = new StringBuilder("\n", 4 * 1024 * 1024);
12 | private Process _process;
13 |
14 | protected abstract void AppendOutput(string output, bool isError);
15 |
16 | public abstract void OnClose();
17 |
18 | public abstract void OnError();
19 |
20 | public void Run()
21 | {
22 | CreateCmdProcess();
23 | RegisterStandardOutputListener();
24 | RegisterStandardErrorListener();
25 | }
26 |
27 | public void StopScript()
28 | {
29 | NativeConsoleUtilities.StopProgramByAttachingToItsConsoleAndIssuingCtrlCEvent(_process);
30 | }
31 |
32 | public bool WriteLine(string command)
33 | {
34 | try
35 | {
36 | _process.StandardInput.WriteLine(command);
37 | return true;
38 | }
39 | catch (Exception exception)
40 | {
41 | Debug.WriteLine(exception.ToString());
42 | return false;
43 | }
44 | }
45 |
46 | private void RegisterStandardOutputListener()
47 | {
48 | var outputThread = new Thread(new ParameterizedThreadStart(delegate
49 | {
50 | var buffer = new char[1024 * 512];
51 | while (true)
52 | {
53 | var count = _process.StandardOutput.Read(buffer, 0, buffer.Length);
54 | lock (_output)
55 | {
56 | _output.Append(buffer, 0, count);
57 | AppendOutput(new string(buffer, 0, count), false);
58 | }
59 | }
60 |
61 | }));
62 | outputThread.IsBackground = true;
63 | outputThread.Start();
64 | }
65 |
66 | private void RegisterStandardErrorListener()
67 | {
68 | var errorThread = new Thread(new ParameterizedThreadStart(delegate
69 | {
70 | var buffer = new char[1024 * 512];
71 | while (true)
72 | {
73 | var count = _process.StandardError.Read(buffer, 0, buffer.Length);
74 | lock (_output)
75 | {
76 | _output.Append(buffer, 0, count);
77 | AppendOutput(new string(buffer, 0, count), true);
78 | }
79 | OnError();
80 | }
81 | }));
82 | errorThread.IsBackground = true;
83 | errorThread.Start();
84 | }
85 |
86 | private void CreateCmdProcess()
87 | {
88 | _process = Process.Start(new ProcessStartInfo("cmd.exe")
89 | {
90 | CreateNoWindow = true,
91 | UseShellExecute = false,
92 | RedirectStandardOutput = true,
93 | RedirectStandardInput = true,
94 | RedirectStandardError = true
95 | });
96 |
97 | _process.EnableRaisingEvents = true;
98 | _process.Exited += (s, eventArgs) =>
99 | {
100 | OnClose();
101 | };
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Communication/NativeConsoleUtilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Diagnostics;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 |
7 | namespace PowerCmd.Communication
8 | {
9 | public static class NativeConsoleUtilities
10 | {
11 | private static bool _attached = false;
12 |
13 | #region pinvoke
14 |
15 | [DllImport("kernel32.dll", SetLastError = true)]
16 | static extern bool AttachConsole(uint dwProcessId);
17 |
18 | [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
19 | static extern bool FreeConsole();
20 |
21 | [DllImport("kernel32.dll")]
22 | static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
23 | // Delegate type to be used as the Handler Routine for SCCH
24 | delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
25 |
26 | // Enumerated type for the control messages sent to the handler routine
27 | enum CtrlTypes : uint
28 | {
29 | CTRL_C_EVENT = 0,
30 | CTRL_BREAK_EVENT,
31 | CTRL_CLOSE_EVENT,
32 | CTRL_LOGOFF_EVENT = 5,
33 | CTRL_SHUTDOWN_EVENT
34 | }
35 |
36 | [DllImport("kernel32.dll")]
37 | [return: MarshalAs(UnmanagedType.Bool)]
38 | private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
39 |
40 | private enum ShowCommands
41 | {
42 | SW_HIDE = 0,
43 | SW_SHOWNORMAL = 1,
44 | SW_NORMAL = 1,
45 | SW_SHOWMINIMIZED = 2,
46 | SW_SHOWMAXIMIZED = 3,
47 | SW_MAXIMIZE = 3,
48 | SW_SHOWNOACTIVATE = 4,
49 | SW_SHOW = 5,
50 | SW_MINIMIZE = 6,
51 | SW_SHOWMINNOACTIVE = 7,
52 | SW_SHOWNA = 8,
53 | SW_RESTORE = 9,
54 | SW_SHOWDEFAULT = 10,
55 | SW_FORCEMINIMIZE = 11,
56 | SW_MAX = 11
57 | }
58 |
59 | [DllImport("shell32.dll")]
60 | static extern IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, ShowCommands nShowCmd);
61 |
62 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
63 | struct STARTUPINFO
64 | {
65 | public Int32 cb;
66 | public string lpReserved;
67 | public string lpDesktop;
68 | public string lpTitle;
69 | public Int32 dwX;
70 | public Int32 dwY;
71 | public Int32 dwXSize;
72 | public Int32 dwYSize;
73 | public Int32 dwXCountChars;
74 | public Int32 dwYCountChars;
75 | public Int32 dwFillAttribute;
76 | public Int32 dwFlags;
77 | public Int16 wShowWindow;
78 | public Int16 cbReserved2;
79 | public IntPtr lpReserved2;
80 | public IntPtr hStdInput;
81 | public IntPtr hStdOutput;
82 | public IntPtr hStdError;
83 | }
84 |
85 | [StructLayout(LayoutKind.Sequential)]
86 | internal struct PROCESS_INFORMATION
87 | {
88 | public IntPtr hProcess;
89 | public IntPtr hThread;
90 | public int dwProcessId;
91 | public int dwThreadId;
92 | }
93 |
94 | [StructLayout(LayoutKind.Sequential)]
95 | public struct SECURITY_ATTRIBUTES
96 | {
97 | public int nLength;
98 | public IntPtr lpSecurityDescriptor;
99 | public int bInheritHandle;
100 | }
101 |
102 | [DllImport("kernel32.dll")]
103 | static extern bool CreateProcess(string lpApplicationName,
104 | string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
105 | ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles,
106 | uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
107 | [In] ref STARTUPINFO lpStartupInfo,
108 | out PROCESS_INFORMATION lpProcessInformation);
109 |
110 | private const int WM_VSCROLL = 277;
111 | private const int SB_BOTTOM = 7;
112 |
113 | [DllImport("kernel32.dll", SetLastError = true)]
114 | [return: MarshalAs(UnmanagedType.Bool)]
115 | public static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);
116 |
117 | [DllImport("user32.dll")]
118 | private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
119 |
120 | [DllImport("user32.dll")]
121 | [return: MarshalAs(UnmanagedType.Bool)]
122 | private static extern bool ShowWindow(IntPtr hWnd, ShowCommands nCmdShow);
123 |
124 | [DllImport("user32.dll", SetLastError = true)]
125 | private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
126 |
127 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
128 | public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
129 |
130 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
131 | public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
132 |
133 | public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
134 |
135 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
136 | [return: MarshalAs(UnmanagedType.Bool)]
137 | public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList lParam);
138 |
139 | [DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
140 | private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
141 |
142 | [return: MarshalAs(UnmanagedType.Bool)]
143 | [DllImport("user32.dll", SetLastError = true)]
144 | static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
145 |
146 | private const int VK_CONTROL = 0x11;
147 | private const int WM_KEYDOWN = 0x100;
148 | private const int WM_CHAR = 0x102;
149 | private const int WM_KEYUP = 0x101;
150 | private const int VK_CANCEL = 0x03;
151 | private const int VK_C = 0x0043;
152 |
153 | #endregion pinvoke
154 |
155 | public static void StopProgramByAttachingToItsConsoleAndIssuingCtrlCEvent(Process proc)
156 | {
157 | if (!_attached)
158 | {
159 | if (AttachConsole((uint)proc.Id))
160 | {
161 | SetConsoleCtrlHandler(null, true);
162 | _attached = true;
163 | }
164 | else
165 | return;
166 | }
167 |
168 | GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
169 | }
170 | }
171 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Communication/UiCmdProcessController.cs:
--------------------------------------------------------------------------------
1 | using PowerCmd.ViewModels;
2 | using PowerCmd.Views;
3 |
4 | namespace PowerCmd.Communication
5 | {
6 | public class UiCmdProcessController : CmdProcessController
7 | {
8 | private readonly MainWindow _view;
9 | private readonly MainWindowModel _model;
10 |
11 | public UiCmdProcessController(MainWindow view, MainWindowModel model)
12 | {
13 | _view = view;
14 | _model = model;
15 | }
16 |
17 | protected override void AppendOutput(string output, bool isError)
18 | {
19 | _view.AppendOutput(output, isError);
20 | }
21 |
22 | public override void OnClose()
23 | {
24 | _view.Dispatcher.InvokeAsync(() =>
25 | {
26 | _view.Close();
27 | });
28 | }
29 |
30 | public override void OnError()
31 | {
32 | _view.Dispatcher.InvokeAsync(() =>
33 | {
34 | if (_model.LastCommand != null)
35 | _model.LastCommand.HasErrors = true;
36 | });
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Converters/ColorToBrushConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 | using System.Windows.Media;
5 |
6 | namespace PowerCmd.Converters
7 | {
8 | public class ColorToBrushConverter : IValueConverter
9 | {
10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
11 | {
12 | if (value != null)
13 | return new SolidColorBrush((Color)value);
14 | return null;
15 | }
16 |
17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
18 | {
19 | throw new NotImplementedException();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Converters/ErrorBrushConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 | using System.Windows.Media;
5 |
6 | namespace PowerCmd.Converters
7 | {
8 | public class ErrorBrushConverter : IValueConverter
9 | {
10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
11 | {
12 | if (value is bool && (bool)value == true)
13 | return new SolidColorBrush(Colors.IndianRed);
14 | return new SolidColorBrush(Colors.LightGreen);
15 | }
16 |
17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
18 | {
19 | throw new NotImplementedException();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Extensions/Highlighting/PathColorizer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text.RegularExpressions;
3 | using System.Windows.Media;
4 | using ICSharpCode.AvalonEdit.Rendering;
5 |
6 | namespace PowerCmd.Extensions.Highlighting
7 | {
8 | internal class PathColorizer : DocumentColorizingTransformer
9 | {
10 | private SolidColorBrush _greenBrush;
11 |
12 | public PathColorizer()
13 | {
14 | _greenBrush = new SolidColorBrush(Color.FromRgb(79, 182, 54));
15 | }
16 |
17 | protected override void ColorizeLine(ICSharpCode.AvalonEdit.Document.DocumentLine line)
18 | {
19 | string lineText = CurrentContext.Document.GetText(line);
20 |
21 | var match = Regex.Match(lineText, "^(.*)>", RegexOptions.Multiline);
22 | if (match.Success)
23 | {
24 | var path = match.Groups[0].Value;
25 | path = path.Remove(path.Length - 1);
26 |
27 | if (Directory.Exists(path))
28 | {
29 | ChangeLinePart(line.Offset, line.Offset+path.Length+1, ApplyChanges);
30 | }
31 | }
32 | }
33 |
34 | void ApplyChanges(VisualLineElement element)
35 | {
36 | element.TextRunProperties.SetForegroundBrush(_greenBrush);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Extensions/OutputAnalyzers/DirCommandAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text.RegularExpressions;
3 | using System.Threading.Tasks;
4 | using PowerCmd.Models;
5 | using PowerCmd.ViewModels;
6 |
7 | namespace PowerCmd.Extensions.OutputAnalyzers
8 | {
9 | public class DirCommandAnalyzer : IOutputAnalyzer
10 | {
11 | public async Task SupportsCommandAsync(CommandExecutionInfo command)
12 | {
13 | return command.Command.ToLowerInvariant().StartsWith("dir") && !command.HasErrors;
14 | }
15 |
16 | public async Task AnalyzeAsync(CommandExecutionInfo command)
17 | {
18 | command.Results.Add(new CommandExecutionResult
19 | {
20 | Key = "Number of Directories",
21 | Value = ReadNumberOfDirectories(command).ToString()
22 | });
23 |
24 | command.Results.Add(new CommandExecutionResult
25 | {
26 | Key = "Number of Files",
27 | Value = ReadNumberOfFiles(command).ToString()
28 | });
29 | }
30 |
31 | private int ReadNumberOfDirectories(CommandExecutionInfo command)
32 | {
33 | return Regex.Matches(command.Output, "", RegexOptions.Multiline)
34 | .Cast()
35 | .Count(match => match.Success);
36 | }
37 |
38 | private int ReadNumberOfFiles(CommandExecutionInfo command)
39 | {
40 | return Regex.Matches(command.Output, "(AM|PM) ", RegexOptions.Multiline)
41 | .Cast()
42 | .Count(match => match.Success);
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Extensions/OutputAnalyzers/IOutputAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using PowerCmd.Models;
3 | using PowerCmd.ViewModels;
4 |
5 | namespace PowerCmd.Extensions.OutputAnalyzers
6 | {
7 | public interface IOutputAnalyzer
8 | {
9 | Task SupportsCommandAsync(CommandExecutionInfo command);
10 |
11 | Task AnalyzeAsync(CommandExecutionInfo command);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Extensions/OutputAnalyzers/MainBrainShowUpdatesCommandAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text.RegularExpressions;
3 | using System.Threading.Tasks;
4 | using PowerCmd.Models;
5 | using PowerCmd.ViewModels;
6 |
7 | namespace PowerCmd.Extensions.OutputAnalyzers
8 | {
9 | public class MainBrainShowUpdatesCommandAnalyzer : IOutputAnalyzer
10 | {
11 | public async Task SupportsCommandAsync(CommandExecutionInfo command)
12 | {
13 | return command.Command.ToLowerInvariant().StartsWith("msbuild /t:su") && !command.HasErrors;
14 | }
15 |
16 | public async Task AnalyzeAsync(CommandExecutionInfo command)
17 | {
18 | command.Results.Add(new CommandExecutionResult
19 | {
20 | Key = "Number of NuGet Package Updates",
21 | Value = ReadNumberOfUpdates(command).ToString()
22 | });
23 | }
24 |
25 | private int ReadNumberOfUpdates(CommandExecutionInfo command)
26 | {
27 | return Regex.Matches(command.Output, "Repository Version:", RegexOptions.Multiline)
28 | .Cast()
29 | .Count(match => match.Success);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Extensions/OutputAnalyzers/MsBuildCommandAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 | using System.Threading.Tasks;
3 | using System.Windows.Media;
4 | using PowerCmd.Models;
5 | using PowerCmd.ViewModels;
6 |
7 | namespace PowerCmd.Extensions.OutputAnalyzers
8 | {
9 | public class MsBuildCommandAnalyzer : IOutputAnalyzer
10 | {
11 | public async Task SupportsCommandAsync(CommandExecutionInfo command)
12 | {
13 | return command.Command.ToLowerInvariant().StartsWith("msbuild") && !command.HasErrors;
14 | }
15 |
16 | public async Task AnalyzeAsync(CommandExecutionInfo command)
17 | {
18 | command.Results.Add(new CommandExecutionResult
19 | {
20 | Key = "Warnings",
21 | Color = Colors.Khaki,
22 | Value = ReadMsBuildCounter(command, "Warning").ToString()
23 | });
24 |
25 | var errors = ReadMsBuildCounter(command, "Error");
26 | command.Results.Add(new CommandExecutionResult
27 | {
28 | Key = "Errors",
29 | Color = Colors.IndianRed,
30 | Value = errors.ToString()
31 | });
32 |
33 | if (errors > 0 || command.Output.Contains("Build FAILED."))
34 | command.HasErrors = true;
35 | }
36 |
37 | private int ReadMsBuildCounter(CommandExecutionInfo command, string type)
38 | {
39 | var count = 0;
40 | foreach (Match match in Regex.Matches(command.Output, " ([0-9]*) " + type + "\\(s\\)", RegexOptions.Multiline))
41 | {
42 | if (match.Success)
43 | {
44 | var matchCount = 0;
45 | if (int.TryParse(match.Groups[1].Value, out matchCount))
46 | count += matchCount;
47 | }
48 | }
49 | return count;
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Extensions/SuggestionProviders/CdHistoryProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using PowerCmd.ViewModels;
6 |
7 | namespace PowerCmd.Extensions.SuggestionProviders
8 | {
9 | public class CdHistoryProvider : ISuggestionProvider
10 | {
11 | private readonly MainWindowModel _model;
12 |
13 | public CdHistoryProvider(MainWindowModel model)
14 | {
15 | _model = model;
16 | }
17 |
18 | public bool SupportsCommand(string command)
19 | {
20 | return command == "cd" || command.StartsWith("cd ");
21 | }
22 |
23 | public async Task> GetSuggestionsAsync(string command)
24 | {
25 | try
26 | {
27 | command = command.Replace("\\", "/");
28 |
29 | var cwd = _model.CurrentWorkingDirectory;
30 | var segments = command.Length > 3
31 | ? command.Substring(3).Split('/')
32 | : new string[] { };
33 |
34 | var prefix = string.Join("/", segments.Take(segments.Length - 1));
35 | cwd = segments.Length > 1 ? (prefix.Contains(":") ? prefix + "/" : Path.Combine(cwd, prefix)) : cwd;
36 |
37 | var directories = (await Task.Run(() => Directory.GetDirectories(cwd)))
38 | .Select(p => "cd " + (!string.IsNullOrEmpty(prefix) ? prefix + "/" + Path.GetFileName(p) : Path.GetFileName(p))).ToList();
39 |
40 | if (string.IsNullOrEmpty(prefix))
41 | directories.Insert(0, "cd ..");
42 |
43 | return directories;
44 | }
45 | catch
46 | {
47 | return new string[] { };
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Extensions/SuggestionProviders/DefaultSuggestionProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using PowerCmd.ViewModels;
7 |
8 | namespace PowerCmd.Extensions.SuggestionProviders
9 | {
10 | public class DefaultSuggestionProvider : ISuggestionProvider
11 | {
12 | private readonly MainWindowModel _model;
13 |
14 | public DefaultSuggestionProvider(MainWindowModel model)
15 | {
16 | _model = model;
17 | }
18 |
19 | public bool SupportsCommand(string command)
20 | {
21 | return true;
22 | }
23 |
24 | public async Task> GetSuggestionsAsync(string command)
25 | {
26 | var suggestions = new List();
27 | try
28 | {
29 | suggestions.AddRange(Directory.GetFiles(_model.CurrentWorkingDirectory).Select(Path.GetFileName));
30 | }
31 | catch { }
32 | suggestions.AddRange(_model.CommandHistory.Select(c => c.Command));
33 | return suggestions;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Extensions/SuggestionProviders/ISuggestionProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 |
4 | namespace PowerCmd.Extensions.SuggestionProviders
5 | {
6 | public interface ISuggestionProvider
7 | {
8 | bool SupportsCommand(string command);
9 |
10 | Task> GetSuggestionsAsync(string command);
11 | }
12 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Models/CommandButton.cs:
--------------------------------------------------------------------------------
1 | namespace PowerCmd.Models
2 | {
3 | public class CommandButton
4 | {
5 | public string Title { get; set; }
6 |
7 | public string Subtitle { get; set; }
8 |
9 | public string Alias { get; set; }
10 |
11 | public string Text { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Models/CommandExecutionInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using MyToolkit.Model;
7 | using PowerCmd.Extensions.OutputAnalyzers;
8 |
9 | namespace PowerCmd.Models
10 | {
11 | public class CommandExecutionInfo : ObservableObject
12 | {
13 | private static readonly List _analyzers = new List
14 | {
15 | new DirCommandAnalyzer(),
16 | new MsBuildCommandAnalyzer(),
17 | new MainBrainShowUpdatesCommandAnalyzer()
18 | };
19 |
20 | private string _command;
21 | private string _workingDirectory;
22 | private readonly StringBuilder _output = new StringBuilder(128 * 1024);
23 |
24 | private DateTime _startTime;
25 | private DateTime? _endTime;
26 | private bool _hasErrors;
27 |
28 | public CommandExecutionInfo(string command, string workingDirectory)
29 | {
30 | Command = command;
31 | WorkingDirectory = workingDirectory;
32 | StartTime = DateTime.Now;
33 | }
34 |
35 | /// Gets or sets the command.
36 | public string Command
37 | {
38 | get { return _command; }
39 | set { Set(ref _command, value); }
40 | }
41 |
42 | /// Gets or sets the working directory.
43 | public string WorkingDirectory
44 | {
45 | get { return _workingDirectory; }
46 | set { Set(ref _workingDirectory, value); }
47 | }
48 |
49 | /// Gets or sets the start time.
50 | public DateTime StartTime
51 | {
52 | get { return _startTime; }
53 | set { Set(ref _startTime, value); }
54 | }
55 |
56 | /// Gets or sets the end time.
57 | public DateTime? EndTime
58 | {
59 | get { return _endTime; }
60 | set
61 | {
62 | if (Set(ref _endTime, value))
63 | {
64 | RaisePropertyChanged(() => Duration);
65 | RaisePropertyChanged(() => IsRunning);
66 | }
67 | }
68 | }
69 |
70 | public bool IsRunning => !Duration.HasValue;
71 |
72 | /// Gets or sets a value indicating whether the command has errors.
73 | public bool HasErrors
74 | {
75 | get { return _hasErrors; }
76 | set { Set(ref _hasErrors, value); }
77 | }
78 |
79 | /// Gets or sets the duration.
80 | public TimeSpan? Duration => EndTime.HasValue ? EndTime.Value - StartTime : (TimeSpan?)null;
81 |
82 | public ObservableCollection Results { get; } = new ObservableCollection();
83 |
84 | public string Output => _output.ToString();
85 |
86 | public void AppendOutput(string output)
87 | {
88 | _output.Append(output);
89 | }
90 |
91 | public async Task CompleteAsync()
92 | {
93 | if (!EndTime.HasValue)
94 | {
95 | EndTime = DateTime.Now;
96 |
97 | foreach (var analyzer in _analyzers)
98 | {
99 | if (await analyzer.SupportsCommandAsync(this))
100 | await analyzer.AnalyzeAsync(this);
101 | }
102 | }
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Models/CommandExecutionResult.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Media;
2 |
3 | namespace PowerCmd.Models
4 | {
5 | public class CommandExecutionResult
6 | {
7 | public string Key { get; set; }
8 |
9 | public string Value { get; set; }
10 |
11 | public Color Color { get; set; } = Colors.White;
12 | }
13 | }
--------------------------------------------------------------------------------
/src/PowerCmd/PowerCmd.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {8F22FA02-4448-426E-9CD2-A6E154963AD5}
8 | WinExe
9 | Properties
10 | PowerCmd
11 | PowerCmd
12 | v4.5.1
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 | ..\packages\AvalonEdit.5.0.2\lib\Net40\ICSharpCode.AvalonEdit.dll
40 | True
41 |
42 |
43 | ..\packages\Microsoft.ApplicationInsights.2.0.0\lib\net45\Microsoft.ApplicationInsights.dll
44 | True
45 |
46 |
47 | ..\packages\MyToolkit.2.5.11.0\lib\portable-net45+wp8+win8+wpa81\MyToolkit.dll
48 | True
49 |
50 |
51 | ..\packages\MyToolkit.Extended.2.5.11.0\lib\net45\MyToolkit.Extended.dll
52 | True
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 4.0
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | MSBuild:Compile
72 | Designer
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | MSBuild:Compile
89 | Designer
90 |
91 |
92 | MSBuild:Compile
93 | Designer
94 |
95 |
96 | App.xaml
97 | Code
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | MainWindow.xaml
108 | Code
109 |
110 |
111 |
112 |
113 | Code
114 |
115 |
116 | True
117 | True
118 | Resources.resx
119 |
120 |
121 | True
122 | Settings.settings
123 | True
124 |
125 |
126 | ResXFileCodeGenerator
127 | Resources.Designer.cs
128 |
129 |
130 | Designer
131 |
132 |
133 | SettingsSingleFileGenerator
134 | Settings.Designer.cs
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
150 |
--------------------------------------------------------------------------------
/src/PowerCmd/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | [assembly: AssemblyTitle("PowerCmd")]
4 | [assembly: AssemblyDescription("")]
5 | [assembly: AssemblyCompany("Rico Suter")]
6 | [assembly: AssemblyProduct("PowerCmd")]
7 | [assembly: AssemblyCopyright("Copyright © Rico Suter & Contributors, 2016")]
8 | [assembly: AssemblyVersion("1.25.*")]
9 |
--------------------------------------------------------------------------------
/src/PowerCmd/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace PowerCmd.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PowerCmd.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/PowerCmd/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/src/PowerCmd/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace PowerCmd.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/PowerCmd/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/PowerCmd/Themes/LeftMarginMultiplierConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Data;
10 |
11 | namespace DarkBlendTheme
12 | {
13 | public class LeftMarginMultiplierConverter : IValueConverter
14 | {
15 | public double Length { get; set; }
16 |
17 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
18 | {
19 | var item = value as TreeViewItem;
20 | if (item == null)
21 | return new Thickness(0);
22 |
23 | return new Thickness(Length * item.GetDepth(), 0, 0, 0);
24 | }
25 |
26 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
27 | {
28 | throw new System.NotImplementedException();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/PowerCmd/Themes/TreeViewItemExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Data;
10 | using System.Windows.Media;
11 |
12 | namespace DarkBlendTheme
13 | {
14 | public static class TreeViewItemExtensions
15 | {
16 | public static int GetDepth(this TreeViewItem item)
17 | {
18 | TreeViewItem parent;
19 | while ((parent = GetParent(item)) != null)
20 | {
21 | return GetDepth(parent) + 1;
22 | }
23 | return 0;
24 | }
25 |
26 | private static TreeViewItem GetParent(TreeViewItem item)
27 | {
28 | var parent = VisualTreeHelper.GetParent(item);
29 |
30 | while (!(parent is TreeViewItem || parent is TreeView))
31 | {
32 | if (parent == null) return null;
33 | parent = VisualTreeHelper.GetParent(parent);
34 | }
35 | return parent as TreeViewItem;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/PowerCmd/ViewModels/MainWindowModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Windows.Input;
9 | using MyToolkit.Command;
10 | using MyToolkit.Mvvm;
11 | using MyToolkit.Utilities;
12 | using PowerCmd.Models;
13 |
14 | namespace PowerCmd.ViewModels
15 | {
16 | public class MainWindowModel : ViewModelBase
17 | {
18 | private ObservableCollection _commandButtons;
19 | private string _currentWorkingDirectory;
20 | private bool _isRunning;
21 | private string _rootDirectory = string.Empty;
22 | private IEnumerable _directories;
23 |
24 | public MainWindowModel()
25 | {
26 | OpenCurrentWorkingDirectoryInExplorerCommand = new RelayCommand(OpenCurrentWorkingDirectoryInExplorer);
27 | }
28 |
29 | public ICommand OpenCurrentWorkingDirectoryInExplorerCommand { get; private set; }
30 |
31 | public ObservableCollection CommandHistory { get; } = new ObservableCollection();
32 |
33 | /// Gets or sets the root directory.
34 | public string RootDirectory
35 | {
36 | get { return _rootDirectory; }
37 | set
38 | {
39 | if (Set(ref _rootDirectory, value))
40 | LoadDirectoriesAsync();
41 | }
42 | }
43 |
44 | /// Gets or sets the directories.
45 | public IEnumerable Directories
46 | {
47 | get { return _directories; }
48 | set { Set(ref _directories, value); }
49 | }
50 |
51 | /// Gets or sets the command buttons.
52 | public ObservableCollection CommandButtons
53 | {
54 | get { return _commandButtons; }
55 | set { Set(ref _commandButtons, value); }
56 | }
57 |
58 | /// Gets or sets the currentWorkingDirectory.
59 | public string CurrentWorkingDirectory
60 | {
61 | get { return _currentWorkingDirectory; }
62 | set
63 | {
64 | if (Set(ref _currentWorkingDirectory, value))
65 | RaisePropertyChanged(() => CurrentWindowTitle);
66 | }
67 | }
68 |
69 | /// Gets or sets the currentWorkingDirectory.
70 | public string CurrentWindowTitle => "PowerCmd (" + CurrentWorkingDirectory + ") v" + Assembly.GetEntryAssembly().GetName().Version;
71 |
72 | /// Gets or sets a value indicating whether a command is running.
73 | public bool IsRunning
74 | {
75 | get { return _isRunning; }
76 | set
77 | {
78 | if (Set(ref _isRunning, value))
79 | LastCommand?.CompleteAsync();
80 | }
81 | }
82 |
83 | /// Gets or sets the last command.
84 | public CommandExecutionInfo LastCommand => CommandHistory.Any() ? CommandHistory.First() : null;
85 |
86 | /// Gets the application version with build time.
87 | public string ApplicationVersion => "v" + GetType().Assembly.GetVersionWithBuildTime();
88 |
89 | public void RunCommand(string command)
90 | {
91 | if (!IsRunning)
92 | {
93 | IsRunning = true;
94 | CommandHistory.Insert(0, new CommandExecutionInfo(command, CurrentWorkingDirectory));
95 | RaisePropertyChanged(() => LastCommand);
96 | }
97 | }
98 |
99 | private void OpenCurrentWorkingDirectoryInExplorer()
100 | {
101 | Process.Start(CurrentWorkingDirectory);
102 | }
103 |
104 | private void LoadDirectoriesAsync()
105 | {
106 | if ((RootDirectory.Contains("/") || RootDirectory.Contains("\\")) && Directory.Exists(RootDirectory))
107 | {
108 | try
109 | {
110 | Directories = Directory.GetDirectories(RootDirectory).Select(Path.GetFileName);
111 | }
112 | catch
113 | {
114 | Directories = new string[] { };
115 | }
116 | }
117 | else
118 | Directories = new string[] { };
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Views/ComboBoxUtilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using System.Windows.Controls;
5 |
6 | namespace PowerCmd.Views
7 | {
8 | public static class ComboBoxUtilities
9 | {
10 | public static void SelectSuggestion(this ComboBox comboBox)
11 | {
12 | comboBox.Focus();
13 |
14 | var editableTextBox = (TextBox)comboBox.Template.FindName("PART_EditableTextBox", comboBox);
15 | editableTextBox.SelectionStart = comboBox.Text.Length;
16 | editableTextBox.SelectionLength = 0;
17 | }
18 |
19 | public static void AppendText(this ComboBox comboBox, string text)
20 | {
21 | comboBox.Focus();
22 |
23 | var editableTextBox = (TextBox)comboBox.Template.FindName("PART_EditableTextBox", comboBox);
24 | editableTextBox.AppendText(text);
25 | editableTextBox.SelectionStart = comboBox.Text.Length;
26 | editableTextBox.SelectionLength = 0;
27 | }
28 |
29 | public static void SetText(this ComboBox comboBox, string text)
30 | {
31 | comboBox.Focus();
32 | comboBox.Text = text;
33 |
34 | var editableTextBox = (TextBox)comboBox.Template.FindName("PART_EditableTextBox", comboBox);
35 | editableTextBox.SelectionStart = comboBox.Text.Length;
36 | editableTextBox.SelectionLength = 0;
37 | }
38 |
39 | public static async Task SetSuggestionsAsync(this ComboBox comboBox, Func>> suggestionProvider)
40 | {
41 | var editableTextBox = (TextBox)comboBox.Template.FindName("PART_EditableTextBox", comboBox);
42 | var previousText = comboBox.Text;
43 | var previousStart = editableTextBox.SelectionStart;
44 | var command = editableTextBox.SelectionStart >= 0 ? editableTextBox.Text.Substring(0, editableTextBox.SelectionStart) : editableTextBox.Text;
45 |
46 | comboBox.ItemsSource = await suggestionProvider(command);
47 |
48 | if (comboBox.Text != previousText && previousStart >= 0)
49 | {
50 | comboBox.Text = previousText;
51 | editableTextBox.SelectionStart = previousStart;
52 | editableTextBox.SelectionLength = editableTextBox.Text.Length - previousStart;
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/src/PowerCmd/Views/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
89 |
92 |
93 |
94 |
95 |
96 |
99 |
100 |
101 |
102 |
104 |
105 |
106 |
108 |
109 |
110 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
123 |
124 |
125 |
126 |
127 |
129 |
130 |
131 |
132 |
135 |
136 |
137 |
138 |
139 |
141 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
178 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
200 |
201 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
--------------------------------------------------------------------------------
/src/PowerCmd/Views/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 | using System.Threading.Tasks;
9 | using System.Windows;
10 | using System.Windows.Controls;
11 | using System.Windows.Input;
12 | using Microsoft.Win32;
13 | using MyToolkit.Serialization;
14 | using MyToolkit.Storage;
15 | using MyToolkit.Utilities;
16 | using PowerCmd.Communication;
17 | using PowerCmd.Extensions.Highlighting;
18 | using PowerCmd.Extensions.SuggestionProviders;
19 | using PowerCmd.Models;
20 | using PowerCmd.ViewModels;
21 |
22 | namespace PowerCmd.Views
23 | {
24 | public partial class MainWindow : Window
25 | {
26 | private int _maxOutputLength = 1024 * 32;
27 | private UiCmdProcessController _cmdProcessController;
28 | private readonly List _suggestionProviders;
29 |
30 | public MainWindow()
31 | {
32 | InitializeComponent();
33 |
34 | Loaded += OnLoaded;
35 | Closed += OnClosed;
36 |
37 | CheckForApplicationUpdate();
38 | LoadSettings();
39 | ApplyHighlighter();
40 |
41 | Activated += (sender, args) => { Input.Focus(); };
42 |
43 | _suggestionProviders = new List
44 | {
45 | new CdHistoryProvider(Model),
46 | new DefaultSuggestionProvider(Model)
47 | };
48 |
49 | SizeChanged += OnSizeChanged;
50 | }
51 |
52 | private void OnSizeChanged(object sender, SizeChangedEventArgs args)
53 | {
54 | BorderThickness = WindowState == WindowState.Maximized ? new Thickness(8) : new Thickness(0);
55 | }
56 |
57 | private void LoadSettings()
58 | {
59 | Model.RootDirectory = ApplicationSettings.GetSetting("RootDirectory", "C:/");
60 |
61 | Width = ApplicationSettings.GetSetting("WindowWidth", Width);
62 | Height = ApplicationSettings.GetSetting("WindowHeight", Height);
63 | Left = ApplicationSettings.GetSetting("WindowLeft", Left);
64 | Top = ApplicationSettings.GetSetting("WindowTop", Top);
65 | WindowState = ApplicationSettings.GetSetting("WindowState", WindowState);
66 |
67 | if (Left == double.NaN)
68 | WindowStartupLocation = WindowStartupLocation.CenterScreen;
69 |
70 | var defaultCommandButtons = CreateDefaultCommandButtons();
71 | try
72 | {
73 | Model.CommandButtons = new ObservableCollection(ApplicationSettings.GetSettingWithXmlSerializer("CommandButtons", defaultCommandButtons));
74 | }
75 | catch
76 | {
77 | Model.CommandButtons = new ObservableCollection(defaultCommandButtons);
78 | }
79 | }
80 |
81 | private static List CreateDefaultCommandButtons()
82 | {
83 | var defaultButtons = new List
84 | {
85 | new CommandButton
86 | {
87 | Title = "VS2015",
88 | Subtitle = "Developer Prompt",
89 | Alias = "vs2015",
90 | Text = @"""C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\VsDevCmd.bat"""
91 | },
92 | new CommandButton
93 | {
94 | Title = "VS2013",
95 | Subtitle = "Developer Prompt",
96 | Alias = "vs2013",
97 | Text = @"""C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat"""
98 | },
99 | new CommandButton
100 | {
101 | Title = "VS2012",
102 | Subtitle = "Developer Prompt",
103 | Alias = "vs2012",
104 | Text = @"""C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Tools\VsDevCmd.bat"""
105 | }
106 | };
107 | return defaultButtons;
108 | }
109 |
110 | private async void CheckForApplicationUpdate()
111 | {
112 | var updater = new ApplicationUpdater(
113 | "PowerCmd.msi",
114 | GetType().Assembly,
115 | "http://rsuter.com/Projects/PowerCmd/updates.php");
116 |
117 | await updater.CheckForUpdate(this);
118 | }
119 |
120 | public MainWindowModel Model => (MainWindowModel)Resources["Model"];
121 |
122 | private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
123 | {
124 | Input.Focus();
125 |
126 | var args = Environment.GetCommandLineArgs();
127 | var currentDirectory = args.Length > 1 ? args[1] : ApplicationSettings.GetSetting("CurrentDirectory", "C:/");
128 | if (Directory.Exists(currentDirectory))
129 | {
130 | Directory.SetCurrentDirectory(currentDirectory);
131 | Model.CurrentWorkingDirectory = currentDirectory;
132 | }
133 | else
134 | Model.CurrentWorkingDirectory = Directory.GetCurrentDirectory();
135 |
136 | _cmdProcessController = new UiCmdProcessController(this, Model);
137 | _cmdProcessController.Run();
138 | }
139 |
140 | private void OnClosed(object sender, EventArgs eventArgs)
141 | {
142 | var match = Regex.Match(Output.Text, "^.*?(\n(.*))>$", RegexOptions.Multiline);
143 | if (match.Success)
144 | ApplicationSettings.SetSetting("CurrentDirectory", match.Groups[2].Value, false, true);
145 |
146 | ApplicationSettings.SetSetting("RootDirectory", Model.RootDirectory);
147 |
148 | ApplicationSettings.SetSetting("WindowWidth", Width);
149 | ApplicationSettings.SetSetting("WindowHeight", Height);
150 | ApplicationSettings.SetSetting("WindowLeft", Left);
151 | ApplicationSettings.SetSetting("WindowTop", Top);
152 | ApplicationSettings.SetSetting("WindowState", WindowState);
153 |
154 | ApplicationSettings.SetSettingWithXmlSerializer("CommandButtons", new List(Model.CommandButtons));
155 | }
156 |
157 | private void OnInputPreviewKeyUp(object sender, KeyEventArgs e)
158 | {
159 | if (e.Key == Key.Tab)
160 | {
161 | e.Handled = true;
162 | if (Input.Text.StartsWith("cd "))
163 | {
164 | if (!Input.Text.EndsWith("/") && !Input.Text.EndsWith("\\"))
165 | {
166 | if (Directory.Exists(Path.Combine(Model.CurrentWorkingDirectory, Input.Text.Substring(3))))
167 | Input.AppendText("/");
168 | else
169 | SelectFirstSuggestion();
170 | }
171 | else
172 | SelectFirstSuggestion();
173 |
174 | UpdateSuggestions();
175 | }
176 | else
177 | Input.SelectSuggestion();
178 | }
179 | }
180 |
181 | private void SelectFirstSuggestion()
182 | {
183 | var suggestions = (IEnumerable)Input.ItemsSource;
184 | if (suggestions.Any())
185 | Input.SetText(((IEnumerable)Input.ItemsSource).First());
186 | else
187 | Input.SelectSuggestion();
188 | }
189 |
190 | private async void OnInputKeyUp(object sender, KeyEventArgs e)
191 | {
192 | if (e.Key == Key.Enter)
193 | {
194 | var commandButton = Model.CommandButtons.FirstOrDefault(b => b.Alias == Input.Text.ToLowerInvariant());
195 | if (commandButton != null)
196 | Input.Text = commandButton.Text;
197 |
198 | WriteCommand(Input.Text);
199 | Input.Text = "";
200 | }
201 | else if (e.Key == Key.C && Keyboard.IsKeyDown(Key.LeftCtrl))
202 | {
203 | e.Handled = true;
204 | OnStopScript(null, null);
205 | }
206 |
207 | await UpdateSuggestions();
208 | }
209 |
210 | private bool _suggestionsRunning = false;
211 | private bool _suggestionsDirty = false;
212 |
213 | private async Task UpdateSuggestions()
214 | {
215 | if (!_suggestionsRunning)
216 | {
217 | _suggestionsRunning = true;
218 | _suggestionsDirty = false;
219 |
220 | await Input.SetSuggestionsAsync(async (command) =>
221 | {
222 | var suggestionProvider = _suggestionProviders.FirstOrDefault(p => p.SupportsCommand(command));
223 | if (suggestionProvider != null)
224 | return await suggestionProvider.GetSuggestionsAsync(command);
225 |
226 | return new string[] { };
227 | });
228 |
229 | _suggestionsRunning = false;
230 |
231 | if (_suggestionsDirty)
232 | await UpdateSuggestions();
233 | }
234 | else
235 | _suggestionsDirty = true;
236 | }
237 |
238 | private void WriteCommand(string command)
239 | {
240 | if (!Model.IsRunning)
241 | Model.RunCommand(command);
242 |
243 | _cmdProcessController.WriteLine(command);
244 | }
245 |
246 | private void OnCommandButtonClicked(object sender, RoutedEventArgs e)
247 | {
248 | var command = (CommandButton)((Button)sender).Tag;
249 | WriteCommand(command.Text);
250 | Input.Focus();
251 | }
252 |
253 | private void ApplyHighlighter()
254 | {
255 | Output.TextArea.TextView.LineTransformers.Add(new PathColorizer());
256 | }
257 |
258 | private bool _updateRequested = false;
259 | private readonly StringBuilder _outputCache = new StringBuilder();
260 | private DateTime _lastUpdate = DateTime.MinValue;
261 | private bool _wasError = false;
262 |
263 | public async void AppendOutput(string output, bool isError)
264 | {
265 | await Dispatcher.InvokeAsync(async () =>
266 | {
267 | if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 200 || _wasError != isError)
268 | {
269 | if (_wasError != isError)
270 | {
271 | AppendOutputDirectly(_outputCache.ToString(), _wasError);
272 | AppendOutputDirectly(output, isError);
273 | }
274 | else
275 | AppendOutputDirectly(_outputCache + output, isError);
276 |
277 | _outputCache.Clear();
278 | _lastUpdate = DateTime.Now;
279 | _updateRequested = false;
280 | }
281 | else if (!_updateRequested)
282 | {
283 | _outputCache.Append(output);
284 | _updateRequested = true;
285 | _wasError = isError;
286 |
287 | await Task.Delay(300);
288 | AppendOutput(string.Empty, isError);
289 | }
290 | else
291 | _outputCache.Append(output);
292 | });
293 | }
294 |
295 | private async void AppendOutputDirectly(string output, bool isError)
296 | {
297 | if (string.IsNullOrEmpty(output))
298 | return;
299 |
300 | Model.LastCommand?.AppendOutput(output);
301 |
302 | if (!isError)
303 | {
304 | var currentWorkingDirectory = TryFindCurrentWorkingDirectory(output);
305 | if (currentWorkingDirectory != null)
306 | {
307 | Model.CurrentWorkingDirectory = currentWorkingDirectory;
308 | Model.IsRunning = false;
309 | }
310 | else
311 | Model.IsRunning = true;
312 | }
313 |
314 | Output.BeginChange();
315 | Output.AppendText(output);
316 | if (Output.Document.TextLength > _maxOutputLength)
317 | Output.Document.Remove(0, Output.Document.TextLength - _maxOutputLength);
318 | Output.EndChange();
319 |
320 | Output.ScrollToVerticalOffset(double.MaxValue);
321 |
322 | // TODO: Remove hack (used to always scroll to end)
323 | await Task.Delay(1000);
324 | Output.ScrollToVerticalOffset(double.MaxValue);
325 | }
326 |
327 | private string TryFindCurrentWorkingDirectory(string text)
328 | {
329 | var match = Regex.Match("\n" + text, "^.*?(\n(.*))>$", RegexOptions.Multiline);
330 | if (match.Success)
331 | {
332 | var path = match.Groups[2].Value;
333 | if (Directory.Exists(path))
334 | return path;
335 | }
336 | return null;
337 | }
338 |
339 | private void OnSaveCommandButtons(object sender, RoutedEventArgs e)
340 | {
341 | var dlg = new SaveFileDialog();
342 | dlg.Filter = "PowerCmd command buttons (*.pcmdb)|*.pcmdb";
343 | dlg.RestoreDirectory = true;
344 | dlg.AddExtension = true;
345 | if (dlg.ShowDialog() == true)
346 | File.WriteAllText(dlg.FileName, XmlSerialization.Serialize(Model.CommandButtons.ToList()));
347 | }
348 |
349 | private void OnLoadCommandButtons(object sender, RoutedEventArgs e)
350 | {
351 | var dlg = new OpenFileDialog();
352 | dlg.Title = "Open PowerCmd command buttons file";
353 | dlg.Filter = "PowerCmd command buttons (*.pcmdb)|*.pcmdb";
354 | dlg.RestoreDirectory = true;
355 | if (dlg.ShowDialog() == true)
356 | {
357 | Model.CommandButtons = new ObservableCollection(
358 | XmlSerialization.Deserialize>(File.ReadAllText(dlg.FileName))
359 | );
360 | }
361 | }
362 |
363 | private void OnStopScript(object sender, RoutedEventArgs e)
364 | {
365 | _cmdProcessController.StopScript();
366 | }
367 |
368 | private void OnCopyPath(object sender, RoutedEventArgs e)
369 | {
370 | Clipboard.SetText(Model.CurrentWorkingDirectory);
371 | }
372 |
373 | private void OnDirectoryDoubleClick(object sender, MouseButtonEventArgs e)
374 | {
375 | var listBox = (ListBox)sender;
376 | if (listBox.SelectedItem != null)
377 | {
378 | var directory = listBox.SelectedItem.ToString();
379 | listBox.SelectedItem = null;
380 |
381 | var command = "cd \"" + Path.Combine(Model.RootDirectory, directory) + "\"";
382 | WriteCommand(command);
383 |
384 | Input.Focus();
385 | }
386 | }
387 |
388 | private void OnDirectoryKeyUp(object sender, KeyEventArgs e)
389 | {
390 | if (e.Key == Key.Enter)
391 | OnDirectoryDoubleClick(sender, null);
392 | }
393 |
394 | private void OnClose(object sender, RoutedEventArgs e)
395 | {
396 | Close();
397 | }
398 |
399 | private void OnMinimize(object sender, RoutedEventArgs e)
400 | {
401 | WindowState = WindowState.Minimized;
402 | }
403 |
404 | private void OnMaximize(object sender, RoutedEventArgs e)
405 | {
406 | if (WindowState != WindowState.Maximized)
407 | WindowState = WindowState.Maximized;
408 | else
409 | WindowState = WindowState.Normal;
410 | }
411 | }
412 | }
413 |
--------------------------------------------------------------------------------
/src/PowerCmd/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------