/// <summary> /// This helper is used to work around a reported, but unreproducable, bug. The constructor /// of DispatcherTimer is throwing an exception claiming a millisecond time greater /// than int.MaxValue is being passed to the constructor. /// /// This is clearly not possible given the input is an int value. However after multiple user /// reports it's clear the exception is getting triggered. /// /// The only semi-plausible idea I can come up with is a floating point conversion issue. Given /// that the input to Timespan is int and the compared value is double it's possible that a /// conversion / rounding issue is causing int.MaxValue to become int.MaxValue + 1. /// /// Either way though need to guard against this case to unblock users. /// /// https://github.com/jaredpar/VsVim/issues/631 /// https://github.com/jaredpar/VsVim/issues/1860 /// </summary> private static DispatcherTimer CreateBlinkTimer(IProtectedOperations protectedOperations, EventHandler onCaretBlinkTimer) { var caretBlinkTime = GetCaretBlinkTime(); var caretBlinkTimeSpan = new TimeSpan(0, 0, 0, 0, caretBlinkTime ?? int.MaxValue); try { var blinkTimer = new DispatcherTimer( caretBlinkTimeSpan, DispatcherPriority.Normal, protectedOperations.GetProtectedEventHandler(onCaretBlinkTimer), Dispatcher.CurrentDispatcher) { IsEnabled = caretBlinkTime != null }; return(blinkTimer); } catch (ArgumentOutOfRangeException) { // Hit the bug ... just create a simple timer with a default interval. VimTrace.TraceError("Error creating BlockCaret DispatcherTimer"); var blinkTimer = new DispatcherTimer( TimeSpan.FromSeconds(2), DispatcherPriority.Normal, protectedOperations.GetProtectedEventHandler(onCaretBlinkTimer), Dispatcher.CurrentDispatcher) { IsEnabled = true }; return(blinkTimer); } }
private void SaveColors() { if (Site == null) { return; } try { var guid = Guid.Parse("{A27B4E24-A735-4d1d-B8E7-9716E1E3D8E0}"); var flags = __FCSTORAGEFLAGS.FCSF_LOADDEFAULTS | __FCSTORAGEFLAGS.FCSF_PROPAGATECHANGES; var vsStorage = (IVsFontAndColorStorage)(Site.GetService(typeof(SVsFontAndColorStorage))); ErrorHandler.ThrowOnFailure(vsStorage.OpenCategory(ref guid, (uint)flags)); foreach (var colorInfo in _colorMap.Values) { if (colorInfo.OriginalColor == colorInfo.Color) { continue; } SaveColor(vsStorage, colorInfo.ColorKey, colorInfo.Color); } ErrorHandler.ThrowOnFailure(vsStorage.CloseCategory()); } catch (Exception ex) { VimTrace.TraceError("Unable to save colors: {0}", ex.ToString()); } }
private bool GetIsLastLineVisible() { // Check whether the last line is visible. try { var textViewLines = _wpfTextView.TextViewLines; if (textViewLines != null && textViewLines.IsValid) { var lastVisibleTextViewLine = textViewLines.LastVisibleLine; var lastVisibleLine = lastVisibleTextViewLine.Start.GetContainingLine(); var lastVisibleLineNumber = lastVisibleLine.LineNumber; var snapshot = lastVisibleLine.Snapshot; if (lastVisibleLineNumber + 1 == snapshot.LineCount) { // Make sure the whole line is visible. var endOfLastVisibleLine = lastVisibleTextViewLine.EndIncludingLineBreak; if (endOfLastVisibleLine.Position == snapshot.Length) { return(true); } } } } catch (InvalidOperationException ex) { VimTrace.TraceError(ex); } return(false); }
private void LoadColorsCore(IVsFontAndColorStorage vsStorage) { foreach (var colorKey in ColorKeyList) { ColorInfo colorInfo; try { var color = LoadColor(vsStorage, colorKey); colorInfo = new ColorInfo(colorKey, color); } catch (Exception ex) { VimTrace.TraceError(ex); colorInfo = new ColorInfo(colorKey, Color.Black, isValid: false); } _colorMap[colorKey] = colorInfo; } }
private void LoadColors() { if (Site == null) { return; } try { var guid = Guid.Parse("{A27B4E24-A735-4d1d-B8E7-9716E1E3D8E0}"); var flags = __FCSTORAGEFLAGS.FCSF_LOADDEFAULTS | __FCSTORAGEFLAGS.FCSF_NOAUTOCOLORS; var vsStorage = (IVsFontAndColorStorage)(Site.GetService(typeof(SVsFontAndColorStorage))); ErrorHandler.ThrowOnFailure(vsStorage.OpenCategory(ref guid, (uint)flags)); LoadColorsCore(vsStorage); ErrorHandler.ThrowOnFailure(vsStorage.CloseCategory()); } catch (Exception ex) { VimTrace.TraceError("Unable to load colors: {0}", ex.ToString()); } }
private IWpfTextViewLine GetTextViewLineContainingPoint(VirtualSnapshotPoint caretPoint) { try { if (!_textView.IsClosed && !_textView.InLayout) { var textViewLines = _textView.TextViewLines; if (textViewLines != null && textViewLines.IsValid) { var textViewLine = textViewLines.GetTextViewLineContainingBufferPosition(caretPoint.Position); if (textViewLine != null && textViewLine.IsValid) { return(textViewLine); } } } } catch (InvalidOperationException ex) { VimTrace.TraceError(ex); } return(null); }
/// <summary> /// Try and process the KeyInput from the Exec method. This method decides whether or not /// a key should be processed directly by IVimBuffer or if should be going through /// IOleCommandTarget. Generally the key is processed by IVimBuffer but for many intellisense /// scenarios we want the key to be routed to Visual Studio directly. Issues to consider /// here are ... /// /// - How should the KeyInput participate in Macro playback? /// - Does both VsVim and Visual Studio need to process the key (Escape mainly) /// /// </summary> private bool TryProcessWithBuffer(KeyInput keyInput) { // If, for some reason, keyboard input is being routed to the text // view but it isn't focused, focus it here. void CheckFocus() { if (_vimBuffer.TextView is IWpfTextView wpfTextView && !wpfTextView.VisualElement.IsFocused) { VimTrace.TraceError("forcing focus back to text view"); wpfTextView.VisualElement.Focus(); } } // If the IVimBuffer can't process it then it doesn't matter if (!_vimBuffer.CanProcess(keyInput)) { return(false); } // In the middle of a word completion session let insert mode handle the input. It's // displaying the intellisense itself and this method is meant to let custom intellisense // operate normally if (_vimBuffer.ModeKind == ModeKind.Insert && _vimBuffer.InsertMode.ActiveWordCompletionSession.IsSome()) { return(_vimBuffer.Process(keyInput).IsAnyHandled); } // If we are in a peek definition window and normal mode we need to let the Escape key // pass on to the next command target. This is necessary to close the peek definition // window if (_vimBuffer.ModeKind == ModeKind.Normal && _textView.IsPeekView() && keyInput == KeyInputUtil.EscapeKey) { return(false); } // If we are in a peek defintion window and in command mode, the // command margin text box won't receive certain keys like // backspace as it normally would. Work around this problem by // generating WPF key events for keys that we know should go to the // command margin text box. Reported in issue #2492. if (_vimBuffer.ModeKind == ModeKind.Command && _textView.IsPeekView() && TryProcessWithWpf(keyInput)) { return(true); } // The only time we actively intercept keys and route them through IOleCommandTarget // is when one of the IDisplayWindowBroker windows is active // // In those cases if the KeyInput is a command which should be handled by the // display window we route it through IOleCommandTarget to get the proper // experience for those features if (!_broker.IsAnyDisplayActive()) { CheckFocus(); return(_vimBuffer.Process(keyInput).IsAnyHandled); } // Next we need to consider here are Key mappings. The CanProcess and Process APIs // will automatically map the KeyInput under the hood at the IVimBuffer level but // not at the individual IMode. Have to manually map here and test against the // mapped KeyInput if (!TryGetSingleMapping(keyInput, out KeyInput mapped)) { return(_vimBuffer.Process(keyInput).IsAnyHandled); } bool handled; if (IsDisplayWindowKey(mapped)) { // If the key which actually needs to be processed is a display window key, say // down, up, etc..., then forward it on to the next IOleCommandTarget. It is responsible // for mapping that key to action against the display window handled = ErrorHandler.Succeeded(_nextOleCommandTarget.Exec(mapped)); } else { CheckFocus(); // Intentionally using keyInput here and not mapped. Process will do mapping on the // provided input hence we should be using the original keyInput here not mapped handled = _vimBuffer.Process(keyInput).IsAnyHandled; } // The Escape key should always dismiss the active completion session. However Vim // itself is mostly ignorant of display windows and typically won't dismiss them // as part of processing Escape (one exception is insert mode). Dismiss it here if // it's still active if (mapped.Key == VimKey.Escape && _broker.IsAnyDisplayActive()) { _broker.DismissDisplayWindows(); } return(handled); }