예제 #1
0
        /// <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);
            }
        }
예제 #2
0
        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());
            }
        }
예제 #3
0
 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);
 }
예제 #4
0
        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;
            }
        }
예제 #5
0
        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());
            }
        }
예제 #6
0
 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);
 }
예제 #7
0
        /// <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);
        }