private void showDiscussionForm(DataCache dataCache, ILocalCommitStorage storage, User currentUser, MergeRequestKey mrk, IEnumerable <Discussion> discussions, string title, User author, string webUrl, string customActionFileName) { if (currentUser == null || discussions == null || author == null || currentUser.Id == 0) { return; } bool doesMatchTag(object tag) => tag != null && ((MergeRequestKey)(tag)).Equals(mrk); Form formExisting = WinFormsHelpers.FindFormByTag("DiscussionsForm", doesMatchTag); if (formExisting is DiscussionsForm existingDiscussionsForm) { existingDiscussionsForm.Restore(); Trace.TraceInformation(String.Format("[MainForm] Activated an existing Discussions view for MR {0}", mrk.IId)); return; } addOperationRecord("Rendering discussion contexts has started"); labelOperationStatus.Refresh(); DiscussionsForm form; try { IAsyncGitCommandService git = storage?.Git; AsyncDiscussionLoader discussionLoader = new AsyncDiscussionLoader(mrk, dataCache, async(key, discussionsUpdated) => { if (storage != null && storage.Updater != null) { try { await storage.Updater.StartUpdate(new DiscussionBasedContextProvider(discussionsUpdated), status => onStorageUpdateProgressChange(status, mrk), () => onStorageUpdateStateChange()); } catch (LocalCommitStorageUpdaterException ex) { ExceptionHandlers.Handle("Cannot update a storage on refreshing discussions", ex); } } else { Trace.TraceInformation("[MainForm] User tried to refresh Discussions without a storage"); MessageBox.Show("Cannot update a storage, some context code snippets may be missing. ", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); } }, this); AsyncDiscussionHelper discussionHelper = new AsyncDiscussionHelper(mrk, title, currentUser, _shortcuts); IEnumerable <ICommand> getCommands(ICommandCallback callback) => loadCustomCommands(customActionFileName, callback); DiscussionsForm discussionsForm = new DiscussionsForm( git, currentUser, mrk, discussions, title, author, _colorScheme, discussionLoader, discussionHelper, webUrl, _shortcuts, getCommands) { Tag = mrk }; form = discussionsForm; } catch (NoDiscussionsToShow) { MessageBox.Show("No discussions to show.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); Trace.TraceInformation(String.Format("[MainForm] No discussions to show for MR IID {0}", mrk.IId)); addOperationRecord("No discussions to show"); return; } addOperationRecord("Opening Discussions view has started"); labelOperationStatus.Refresh(); form.Show(); Trace.TraceInformation(String.Format("[MainForm] Opened Discussions for MR IId {0} (at {1})", mrk.IId, (storage?.Path ?? "null"))); addOperationRecord("Discussions view has opened"); ensureMergeRequestInRecentDataCache(mrk); }
async private Task <IEnumerable <FileInternal> > fetchComparisonsAsync(bool isAwaitedUpdate, CommitStorageUpdateContext context) { bool cancelled = _isDisposed; Exception exception = null; List <FileInternal> allFiles = new List <FileInternal>(); List <ComparisonInternal> comparisons = new List <ComparisonInternal>(); async Task doFetch(BaseToHeadsCollection.FlatBaseToHeadInfo baseToHeadInfo) { if (cancelled) { return; } await suspendProcessingOfNonAwaitedUpdate(isAwaitedUpdate); Comparison comparison = await fetchSingleComparisonAsync(baseToHeadInfo.Base.Sha, baseToHeadInfo.Head.Sha); if (comparison == null || _isDisposed) { cancelled = true; return; } try { throwOnBadComparison(comparison); } catch (LocalCommitStorageUpdaterLimitException ex) { if (baseToHeadInfo.Files?.Any() ?? false) { ExceptionHandlers.Handle("Bad Comparison object", ex); traceInformation(String.Format( "[FileStorageUpdater] Applying manual file comparison for {0} files", baseToHeadInfo.Files.Count())); foreach (BaseToHeadsCollection.RelativeFileInfo fileInfo in baseToHeadInfo.Files) { allFiles.Add(new FileInternal(fileInfo.OldPath, baseToHeadInfo.Base.Sha)); allFiles.Add(new FileInternal(fileInfo.NewPath, baseToHeadInfo.Head.Sha)); } return; } exception = ex; cancelled = true; return; } IEnumerable <DiffStruct> filteredDiffs = filterDiffs(isAwaitedUpdate, comparison.Diffs, baseToHeadInfo.Base.Sha, baseToHeadInfo.Head.Sha, baseToHeadInfo.Files); if (filteredDiffs != null && filteredDiffs.Any()) { comparisons.Add(new ComparisonInternal(filteredDiffs, baseToHeadInfo.Base.Sha, baseToHeadInfo.Head.Sha)); } } await TaskUtils.RunConcurrentFunctionsAsync(context.BaseToHeads.Flatten(), doFetch, () => getComparisonBatchLimits(isAwaitedUpdate), () => cancelled); if (exception != null) { throw exception; } if (cancelled) { return(null); } Action <string> traceFunction = traceDebug; if (isAwaitedUpdate) { traceFunction = traceInformation; } traceFunction(String.Format("Got {0} comparisons, isAwaitedUpdate={1}", comparisons.Count(), isAwaitedUpdate.ToString())); foreach (ComparisonInternal comparison in comparisons) { traceFunction(String.Format("{0} vs {1} ({2} files)", comparison.BaseSha, comparison.HeadSha, comparison.Diffs.Count())); } allFiles.AddRange(comparisons.SelectMany(x => extractFilesFromComparison(x))); return(allFiles); }
private void processDiffQueue() { if (!_requestedDiff.Any()) { return; } DiffRequest diffRequest = _requestedDiff.Peek(); try { SnapshotSerializer serializer = new SnapshotSerializer(); Snapshot snapshot; try { snapshot = serializer.DeserializeFromDisk(diffRequest.GitPID); } catch (Exception ex) // Any exception from de-serialization code { ExceptionHandlers.Handle("Cannot read serialized Snapshot object", ex); MessageBox.Show( "Make sure that diff tool was launched from Merge Request Helper which is still running", "Cannot create a discussion", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification); return; } if (_storageFactory == null || _storageFactory.ParentFolder != snapshot.TempFolder) { Trace.TraceWarning("[MainForm] File Storage folder was changed after launching diff tool"); MessageBox.Show("It seems that file storage folder was changed after launching diff tool. " + "Please restart diff tool.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification); return; } Core.Matching.MatchInfo matchInfo; try { DiffArgumentParser diffArgumentParser = new DiffArgumentParser(diffRequest.DiffArguments); matchInfo = diffArgumentParser.Parse(getDiffTempFolder(snapshot)); Debug.Assert(matchInfo != null); } catch (ArgumentException ex) { ExceptionHandlers.Handle("Cannot parse diff tool arguments", ex); MessageBox.Show("Bad arguments passed from diff tool", "Cannot create a discussion", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification); return; } ProjectKey projectKey = new ProjectKey(snapshot.Host, snapshot.Project); ILocalCommitStorage storage = getCommitStorage(projectKey, false); if (storage.Git == null) { Trace.TraceError("[MainForm] storage.Git is null"); Debug.Assert(false); return; } DataCache dataCache = getDataCacheByName(snapshot.DataCacheName); if (dataCache == null || getCurrentUser() == null) { // It is unexpected to get here when we are not connected to a host Debug.Assert(false); return; } if ((dataCache.ConnectionContext?.GetHashCode() ?? 0) != snapshot.DataCacheHashCode) { Trace.TraceWarning("[MainForm] Data Cache was changed after launching diff tool"); MessageBox.Show("It seems that data cache changed seriously after launching diff tool. " + "Please restart diff tool.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification); return; } DiffCallHandler handler = new DiffCallHandler(storage.Git, getCurrentUser(), (mrk) => dataCache.DiscussionCache?.RequestUpdate( mrk, Constants.DiscussionCheckOnNewThreadFromDiffToolInterval, null), (mrk) => dataCache.DiscussionCache?.GetDiscussions(mrk) ?? Array.Empty <Discussion>(), _shortcuts); handler.Handle(matchInfo, snapshot); } finally { if (_requestedDiff.Any()) { _requestedDiff.Dequeue(); BeginInvoke(new Action(() => processDiffQueue())); } } }
private void addCustomActions() { CustomCommandLoader loader = new CustomCommandLoader(this); _customCommands = null; try { string CustomActionsFileName = "CustomActions.xml"; _customCommands = loader.LoadCommands(CustomActionsFileName); } catch (CustomCommandLoaderException ex) { // If file doesn't exist the loader throws, leaving the app in an undesirable state. // Do not try to load custom actions if they don't exist. ExceptionHandlers.Handle("Cannot load custom actions", ex); } _keywords = _customCommands? .Where(x => x is SendNoteCommand) .Select(x => (x as SendNoteCommand).GetBody()) ?? null; if (_customCommands == null) { return; } int id = 0; foreach (ICommand command in _customCommands) { string name = command.GetName(); var button = new System.Windows.Forms.Button { Name = "customAction" + id, Location = new System.Drawing.Point { X = 0, Y = 19 }, Size = new System.Drawing.Size { Width = 72, Height = 32 }, MinimumSize = new System.Drawing.Size { Width = 72, Height = 0 }, Text = name, UseVisualStyleBackColor = true, Enabled = false, TabStop = false, Tag = command.GetDependency() }; toolTip.SetToolTip(button, command.GetHint()); button.Click += async(x, y) => { MergeRequestKey?mergeRequestKey = getMergeRequestKey(null); if (!mergeRequestKey.HasValue) { return; } ITotalTimeCache totalTimeCache = getDataCache(!isSearchMode())?.TotalTimeCache; labelWorkflowStatus.Text = "Command " + name + " is in progress"; try { await command.Run(); } catch (Exception ex) // Whatever happened in Run() { string errorMessage = "Custom action failed"; ExceptionHandlers.Handle(errorMessage, ex); MessageBox.Show(errorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); labelWorkflowStatus.Text = "Command " + name + " failed"; return; } string statusMessage = String.Format("Command {0} completed for merge request !{1} in project {2}", name, mergeRequestKey.Value.IId, mergeRequestKey.Value.ProjectKey.ProjectName); labelWorkflowStatus.Text = statusMessage; Trace.TraceInformation(String.Format("[MainForm] {0}", statusMessage)); if (command.GetStopTimer()) { await onStopTimer(true); onTimerStopped(totalTimeCache); } bool reload = command.GetReload(); if (reload) { requestUpdates(mergeRequestKey, new int[] { Program.Settings.OneShotUpdateFirstChanceDelayMs, Program.Settings.OneShotUpdateSecondChanceDelayMs }); } }; groupBoxActions.Controls.Add(button); id++; } }
private static void onLaunchFromDiffTool(LaunchOptions launchOptions) { LaunchContext context = (launchOptions.SpecialOptions as LaunchOptions.DiffToolModeOptions).LaunchContext; if (context.IsRunningSingleInstance) { Trace.TraceWarning("Merge Request Helper is not running"); MessageBox.Show("Merge Request Helper is not running. Discussion cannot be created", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } IntPtr concurrentDiscussionWindow = context.GetWindowByCaption(Constants.StartNewThreadCaption, false); if (concurrentDiscussionWindow != IntPtr.Zero) { Trace.TraceWarning(String.Format("Found a concurrent {0} window", Constants.StartNewThreadCaption)); Win32Tools.ForceWindowIntoForeground(concurrentDiscussionWindow); return; } int parentToolPID = -1; try { string diffToolName = Path.GetFileNameWithoutExtension(createDiffTool().GetToolCommand()); StorageSupport.LocalCommitStorageType type = ConfigurationHelper.GetPreferredStorageType(Program.Settings); string toolProcessName = type == StorageSupport.LocalCommitStorageType.FileStorage ? diffToolName : "git"; parentToolPID = getParentProcessId(context.CurrentProcess, toolProcessName); } catch (Exception ex) { ExceptionHandlers.Handle("Cannot find parent diff tool process", ex); } if (parentToolPID == -1) { Trace.TraceError("Cannot find parent diff tool process"); MessageBox.Show( "Cannot find parent diff tool process. Discussion cannot be created. Is Merge Request Helper running?", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } string[] argumentsEx = new string[context.Arguments.Length + 1]; Array.Copy(context.Arguments, 0, argumentsEx, 0, context.Arguments.Length); argumentsEx[argumentsEx.Length - 1] = parentToolPID.ToString(); string message = String.Join("|", argumentsEx); IntPtr mainWindow = context.GetWindowByCaption(Constants.MainWindowCaption, true); if (mainWindow == IntPtr.Zero) { Debug.Assert(false); Trace.TraceWarning("Cannot find Main Window"); return; } Win32Tools.SendMessageToWindow(mainWindow, message); }
private static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); var arguments = Environment.GetCommandLineArgs(); if (arguments.Length < 2) { using (Mutex mutex = new Mutex(false, "Global\\" + mutex1_guid)) { if (!mutex.WaitOne(0, false)) { return; } Application.ThreadException += (sender, e) => HandleUnhandledException(e.Exception); setupTraceListener("mrHelper.main.log"); try { Application.Run(new MainForm()); } catch (Exception ex) // whatever unhandled exception { HandleUnhandledException(ex); } } } else if (arguments[1] == "diff") { using (Mutex mutex = new Mutex(false, "Global\\" + mutex2_guid)) { if (!mutex.WaitOne(0, false)) { return; } Application.ThreadException += (sender, e) => HandleUnhandledException(e.Exception); setupTraceListener("mrHelper.diff.log"); DiffArgumentParser diffArgumentParser = new DiffArgumentParser(arguments); DiffToolInfo diffToolInfo; try { diffToolInfo = diffArgumentParser.Parse(); } catch (ArgumentException ex) { ExceptionHandlers.Handle(ex, "Cannot parse diff tool arguments"); MessageBox.Show("Bad arguments, see details in logs", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } int gitPID = mrHelper.Core.Interprocess.Helpers.GetGitParentProcessId(Process.GetCurrentProcess().Id); SnapshotSerializer serializer = new SnapshotSerializer(); Snapshot snapshot; try { snapshot = serializer.DeserializeFromDisk(gitPID); } catch (System.IO.IOException ex) { ExceptionHandlers.Handle(ex, "Cannot de-serialize snapshot"); MessageBox.Show("Cannot create a discussion. " + "Make sure that you use diff tool instance launched from mrHelper and mrHelper is still running."); return; } IInterprocessCallHandler diffCallHandler = new DiffCallHandler(diffToolInfo); try { diffCallHandler.Handle(snapshot); } catch (Exception ex) // whatever unhandled exception { HandleUnhandledException(ex); } } } }