/// <include file='doc\ViewFilter.uex' path='docs/doc[@for="ViewFilter.GetWordExtent"]/*' /> /// <summary>Returns the result of Source.GetWordExtent.</summary> public virtual int GetWordExtent(int line, int index, uint flags, TextSpan[] span) { Debug.Assert(line >= 0 && index >= 0); if (span == null) { NativeHelpers.RaiseComError(NativeMethods.E_INVALIDARG); } else { span[0] = new TextSpan(); } span[0].iStartLine = span[0].iEndLine = line; span[0].iStartIndex = span[0].iEndIndex = index; int start, end; if (!this.source.GetWordExtent(line, index, (WORDEXTFLAGS)flags, out start, out end)) { return(NativeMethods.S_FALSE); } span[0].iStartIndex = start; span[0].iEndIndex = end; TextSpanHelper.MakePositive(ref span[0]); return(NativeMethods.S_OK); }
/// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.InsertSpecificExpansion"]/*' /> public virtual bool InsertSpecificExpansion(IVsTextView view, XmlElement snippet, TextSpan pos, string relativePath) { if (this.expansionActive) { this.EndTemplateEditing(true); } if (this.source.IsCompletorActive) { this.source.DismissCompletor(); } this.view = view; MSXML.IXMLDOMDocument doc = new MSXML.DOMDocumentClass(); if (!doc.loadXML(snippet.OuterXml)) { throw new ArgumentException(doc.parseError.reason); } Guid guidLanguage = this.source.LanguageService.GetLanguageServiceGuid(); TextSpan2 t2 = TextSpanHelper.TextSpan2FromTextSpan(pos); int hr = this.vsExpansion.InsertSpecificExpansion(doc, t2, this, guidLanguage, relativePath, out this.expansionSession); if (hr != NativeMethods.S_OK || this.expansionSession == null) { this.EndTemplateEditing(true); } else { this.expansionActive = true; return(true); } return(false); }
/// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.FormatSpan"]/*' /> public virtual int FormatSpan(IVsTextLines buffer, TextSpan2[] ts) { if (this.source.GetTextLines() != buffer) { throw new System.ArgumentException(SR.GetString(SR.UnknownBuffer), "buffer"); } int rc = NativeMethods.E_NOTIMPL; if (ts != null) { for (int i = 0, n = ts.Length; i < n; i++) { if (this.source.LanguageService.Preferences.EnableFormatSelection) { TextSpan span = TextSpanHelper.TextSpanFromTextSpan2(ts[i]); // We should not merge edits in this case because it might clobber the // $varname$ spans which are markers for yellow boxes. EditArray edits = new EditArray(this.source, this.view, false, SR.GetString(SR.FormatSpan)); this.source.ReformatSpan(edits, span); edits.ApplyEdits(); rc = NativeMethods.S_OK; } } } return(rc); }
/// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.InsertNamedExpansion"]/*' /> public virtual bool InsertNamedExpansion(IVsTextView view, string title, string path, TextSpan pos, bool showDisambiguationUI) { if (this.source.IsCompletorActive) { this.source.DismissCompletor(); } this.view = view; if (this.expansionActive) { this.EndTemplateEditing(true); } TextSpan2 t2 = TextSpanHelper.TextSpan2FromTextSpan(pos); Guid guidLanguage = this.source.LanguageService.GetLanguageServiceGuid(); int hr = this.vsExpansion.InsertNamedExpansion(title, path, t2, this, guidLanguage, showDisambiguationUI ? 1 : 0, out this.expansionSession); if (hr != NativeMethods.S_OK || this.expansionSession == null) { this.EndTemplateEditing(true); return(false); } else if (hr == NativeMethods.S_OK) { this.expansionActive = true; return(true); } return(false); }
/// <include file='doc\ViewFilter.uex' path='docs/doc[@for="ViewFilter.HandleGoto"]/*' /> /// <summary>Handles VsCommands.GotoDefn, VsCommands.GotoDecl and VsCommands.GotoRef by /// calling OnSyncGoto on the Source object and opening the text editor on the resulting /// URL, then scrolling to the resulting span.</summary> public virtual void HandleGoto(VsCommands cmd) { TextSpan ts = new TextSpan(); // Get the caret position NativeMethods.ThrowOnFailure(this.textView.GetCaretPos(out ts.iStartLine, out ts.iStartIndex)); ts.iEndLine = ts.iStartLine; ts.iEndIndex = ts.iStartIndex; // Get the tip text at that location. // Wait, since the user specifically requested this one... TextSpan span; string url = this.source.OnSyncGoto(cmd, this.textView, ts.iEndLine, ts.iEndIndex, out span); if (url == null || url.Trim().Length == 0) // nothing to show { return; } // Open the referenced document, and scroll to the given location. IVsUIHierarchy hierarchy; uint itemID; IVsWindowFrame frame; IVsTextView view; VsShell.OpenDocument(this.service.Site, url, NativeMethods.LOGVIEWID_Code, out hierarchy, out itemID, out frame, out view); if (view != null) { TextSpanHelper.MakePositive(ref span); NativeMethods.ThrowOnFailure(view.EnsureSpanVisible(span)); NativeMethods.ThrowOnFailure(view.SetSelection(span.iStartLine, span.iStartIndex, span.iEndLine, span.iEndIndex)); } }
/// <include file='doc\DocumentTask.uex' path='docs/doc[@for="DocumentTask.OnNavigate"]/*' /> protected override void OnNavigate(EventArgs e) { TextSpan span = this.span; if (textLineMarker != null) { TextSpan[] spanArray = new TextSpan[1]; NativeMethods.ThrowOnFailure(textLineMarker.GetCurrentSpan(spanArray)); span = spanArray[0]; } IVsUIHierarchy hierarchy; uint itemID; IVsWindowFrame docFrame; IVsTextView textView; VsShell.OpenDocument(this.site, this.fileName, NativeMethods.LOGVIEWID_Code, out hierarchy, out itemID, out docFrame, out textView); NativeMethods.ThrowOnFailure(docFrame.Show()); if (textView != null) { NativeMethods.ThrowOnFailure(textView.SetCaretPos(span.iStartLine, span.iStartIndex)); TextSpanHelper.MakePositive(ref span); NativeMethods.ThrowOnFailure(textView.SetSelection(span.iStartLine, span.iStartIndex, span.iEndLine, span.iEndIndex)); NativeMethods.ThrowOnFailure(textView.EnsureSpanVisible(span)); } base.OnNavigate(e); }
/// <include file='doc\EditArray.uex' path='docs/doc[@for="EditArray.Add"]/*' /> /// <summary> /// Add a new atomic edit to the array. The edits cannot intersect each other. /// The spans in each edit must be based on the current state of the buffer, /// and not based on post-edit spans. This EditArray will calculate the /// post edit spans for you. /// </summary> /// <param name="editSpan"></param> public void Add(EditSpan editSpan) { if (editSpan == null) { throw new ArgumentNullException("editSpan"); } for (int i = this.editList.Count - 1; i >= 0; i--) { EditSpan e = (EditSpan)this.editList[i]; if (TextSpanHelper.Intersects(editSpan.Span, e.Span)) { string msg = SR.GetString(SR.EditIntersects, i); #if LANGTRACE Debug.Assert(false, msg); TraceEdits(); #endif throw new System.ArgumentException(msg); } if (TextSpanHelper.StartsAfterStartOf(editSpan.Span, e.Span)) { this.editList.Insert(i + 1, editSpan); return; } } this.editList.Insert(0, editSpan); }
/// <include file='doc\ViewFilter.uex' path='docs/doc[@for="ViewFilter.GetFullDataTipText"]/*' /> /// <summary>This method checks to see if the IVsDebugger is running, and if so, /// calls it to get additional information about the current token and returns a combined result. /// You can return an HRESULT here like TipSuccesses2.TIP_S_NODEFAULTTIP.</summary> public virtual int GetFullDataTipText(string textValue, TextSpan ts, out string fullTipText) { IVsTextLines textLines; fullTipText = textValue; NativeMethods.ThrowOnFailure(this.textView.GetBuffer(out textLines)); // Now, check if the debugger is running and has anything to offer try { Microsoft.VisualStudio.Shell.Interop.IVsDebugger debugger = this.service.GetIVsDebugger(); if (debugger != null && this.mgr.LanguageService.IsDebugging) { TextSpan[] tsdeb = new TextSpan[1] { new TextSpan() }; if (!TextSpanHelper.IsEmpty(ts)) { // While debugging we always want to evaluate the expression user is hovering over NativeMethods.ThrowOnFailure(textView.GetWordExtent(ts.iStartLine, ts.iStartIndex, (uint)WORDEXTFLAGS.WORDEXT_FINDEXPRESSION, tsdeb)); // If it failed to find something, then it means their is no expression so return S_FALSE if (TextSpanHelper.IsEmpty(tsdeb[0])) { return(NativeMethods.S_FALSE); } } string debugTextTip = null; int hr = debugger.GetDataTipValue(textLines, tsdeb, null, out debugTextTip); fullTipText = debugTextTip; if (hr == (int)TipSuccesses2.TIP_S_NODEFAULTTIP) { return(hr); } if (!string.IsNullOrEmpty(debugTextTip) && debugTextTip != textValue) { // The debugger in this case returns "=value [type]" which we can // append to the variable name so we get "x=value[type]" as the full tip. int i = debugTextTip.IndexOf('='); if (i >= 0) { string spacer = (i < debugTextTip.Length - 1 && debugTextTip[i + 1] == ' ') ? " " : ""; fullTipText = textValue + spacer + debugTextTip.Substring(i); } } } #if DEBUG } catch (COMException e) { Trace.WriteLine("COMException: GetDataTipValue, errorcode=" + e.ErrorCode); #else } catch (COMException) { #endif } if (string.IsNullOrEmpty(fullTipText)) { fullTipText = textValue; } return(NativeMethods.S_OK); }
/// <include file='doc\EditArray.uex' path='docs/doc[@for="EditSpan.EditSpan"]/*' /> /// <summary> /// Construct a new edit span object /// </summary> /// <param name="toReplace">The text span to remove from the buffer (can be empty)</param> /// <param name="insertText">The text to insert in it's place (can be null)</param> public EditSpan(TextSpan toReplace, string insertText) { if (!TextSpanHelper.IsPositive(toReplace)) { TextSpanHelper.MakePositive(ref toReplace); } this.span = toReplace; this.text = insertText; this.lineCount = -1; }
/// <include file='doc\Source.uex' path='docs/doc[@for="ViewFilter.GetSelection"]/*' /> /// <summary>Returns the current selection, adjusted to become a positive text span</summary> public TextSpan GetSelection() { //get text range TextSpan[] aspan = new TextSpan[1]; NativeMethods.ThrowOnFailure(this.textView.GetSelectionSpan(aspan)); if (!TextSpanHelper.IsPositive(aspan[0])) { TextSpanHelper.MakePositive(ref aspan[0]); } return(aspan[0]); }
/// <include file='doc\ViewFilter.uex' path='docs/doc[@for="ViewFilter.GetPairExtents"]/*' /> public virtual int GetPairExtents(int line, int index, TextSpan[] span) { Debug.Assert(line >= 0 && index >= 0); if (span == null) { return(NativeMethods.E_INVALIDARG); } this.source.GetPairExtents(this.textView, line, index, out span[0]); TextSpanHelper.MakePositive(ref span[0]); return(NativeMethods.S_OK); }
/// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.InTemplateEditingMode"]/*' /> public virtual TextSpan GetExpansionSpan() { if (this.expansionSession == null) { throw new System.InvalidOperationException(SR.GetString(SR.NoExpansionSession)); } TextSpan2[] pts = new TextSpan2[1]; int hr = this.expansionSession.GetSnippetSpan(pts); if (NativeMethods.Succeeded(hr)) { return(TextSpanHelper.TextSpanFromTextSpan2(pts[0])); } return(new TextSpan()); }
// This method simulates what VS does in debug mode so that we can catch the // errors in managed code before they go to the native debug assert. public static bool ValidSpan(Source src, TextSpan span) { if (!ValidCoord(src, span.iStartLine, span.iStartIndex)) { return(false); } if (!ValidCoord(src, span.iEndLine, span.iEndIndex)) { return(false); } // end must be >= start if (!TextSpanHelper.IsPositive(span)) { return(false); } return(true); }
/// <include file='doc\ViewFilter.uex' path='docs/doc[@for="ViewFilter.ReformatSelection;"]/*' /> public virtual void ReformatSelection() { if (this.CanReformat()) { Debug.Assert(this.source != null); if (this.source != null) { TextSpan ts = GetSelection(); if (TextSpanHelper.IsEmpty(ts)) { // format just this current line. ts.iStartIndex = 0; ts.iEndLine = ts.iStartLine; ts.iEndIndex = this.source.GetLineLength(ts.iStartLine); } EditArray mgr = new EditArray(this.source, this.TextView, true, SR.GetString(SR.FormatSpan)); this.source.ReformatSpan(mgr, ts); mgr.ApplyEdits(); } } }
/// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.FindExpansionByShortcut"]/*' /> /// <summary>Returns S_OK if match found, S_FALSE if expansion UI is shown, and error otherwise</summary> public virtual int FindExpansionByShortcut(IVsTextView view, string shortcut, TextSpan span, bool showDisambiguationUI, out string title, out string path) { if (this.expansionActive) { this.EndTemplateEditing(true); } this.view = view; title = path = null; LanguageService svc = this.source.LanguageService; IVsExpansionManager mgr = svc.Site.GetService(typeof(SVsExpansionManager)) as IVsExpansionManager; if (mgr == null) { return(NativeMethods.E_FAIL); } Guid guidLanguage = svc.GetLanguageServiceGuid(); TextSpan2[] pts = new TextSpan2[1]; pts[0] = TextSpanHelper.TextSpan2FromTextSpan(span); int hr = mgr.GetExpansionByShortcut(this, guidLanguage, shortcut, this.TextView, pts, showDisambiguationUI ? 1 : 0, out path, out title); return(hr); }
public static bool Intersects(TextSpan span1, TextSpan span2) { return(TextSpanHelper.StartsBeforeEndOf(span1, span2) && TextSpanHelper.EndsAfterStartOf(span1, span2)); }
public static bool IsEmbedded(TextSpan span1, TextSpan span2) { return(!TextSpanHelper.IsSameSpan(span1, span2) && TextSpanHelper.StartsAfterStartOf(span1, span2) && TextSpanHelper.EndsBeforeEndOf(span1, span2)); }
const int ChunkThreshold = 1000; // don't combine chunks separate by more than 1000 characters. ArrayList MergeEdits(ArrayList edits) { StringBuilder buffer = new StringBuilder(); EditSpan combined = null; ArrayList merged = new ArrayList(); ArrayList markers = GetTextMarkers(); int markerPos = 0; TextSpan marker = (markers.Count > 0) ? (TextSpan)markers[0] : new TextSpan(); foreach (EditSpan editSpan in edits) { TextSpan span = editSpan.Span; string text = editSpan.Text; if (markerPos < markers.Count && (TextSpanHelper.StartsAfterStartOf(span, marker) || TextSpanHelper.EndsAfterStartOf(span, marker))) { AddCombinedEdit(combined, buffer, merged); if (TextSpanHelper.Intersects(span, marker)) { combined = null; // Have to apply this as a distinct edit operation. merged.Add(editSpan); } else { combined = editSpan; buffer.Append(text); } while (++markerPos < markers.Count) { marker = (TextSpan)markers[markerPos]; if (!TextSpanHelper.StartsAfterStartOf(span, marker) && !TextSpanHelper.EndsAfterStartOf(span, marker)) { break; } } } else if (combined == null) { combined = editSpan; buffer.Append(text); } else { // A little sanity check here, if there are too many characters in between the two // edits, then keep them separate. TextSpan s = combined.Span; string between = this.source.GetText(s.iEndLine, s.iEndIndex, span.iStartLine, span.iStartIndex); if (between.Length > ChunkThreshold) { AddCombinedEdit(combined, buffer, merged); combined = editSpan; buffer.Append(text); } else { // merge edit spans by adding the text in-between the current and previous spans. buffer.Append(between); buffer.Append(text); // and add the new text. s.iEndIndex = span.iEndIndex; s.iEndLine = span.iEndLine; combined.Span = s; } } } AddCombinedEdit(combined, buffer, merged); return(merged); }
/// <include file='doc\ExpansionProvider.uex' path='docs/doc[@for="ExpansionProvider.HandlePreExec"]/*' /> public virtual bool HandlePreExec(ref Guid guidCmdGroup, uint nCmdId, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { if (!this.expansionActive || this.expansionSession == null) { return(false); } this.completorActiveDuringPreExec = this.IsCompletorActive(this.view); if (guidCmdGroup == typeof(VsCommands2K).GUID) { VsCommands2K cmd = (VsCommands2K)nCmdId; #if TRACE_EXEC Trace.WriteLine(String.Format("ExecCommand: {0}", cmd.ToString())); #endif switch (cmd) { case VsCommands2K.CANCEL: if (this.completorActiveDuringPreExec) { return(false); } EndTemplateEditing(true); return(true); case VsCommands2K.RETURN: bool leaveCaret = false; int line = 0, col = 0; if (NativeMethods.Succeeded(this.view.GetCaretPos(out line, out col))) { TextSpan span = GetExpansionSpan(); if (!TextSpanHelper.ContainsExclusive(span, line, col)) { leaveCaret = true; } } if (this.completorActiveDuringPreExec) { return(false); } EndTemplateEditing(leaveCaret); if (leaveCaret) { return(false); } return(true); case VsCommands2K.BACKTAB: if (this.completorActiveDuringPreExec) { return(false); } this.expansionSession.GoToPreviousExpansionField(); return(true); case VsCommands2K.TAB: if (this.completorActiveDuringPreExec) { return(false); } this.expansionSession.GoToNextExpansionField(0); // fCommitIfLast=false return(true); #if TRACE_EXEC case VsCommands2K.TYPECHAR: if (pvaIn != IntPtr.Zero) { Variant v = Variant.ToVariant(pvaIn); char ch = v.ToChar(); Trace.WriteLine(String.Format("TYPECHAR: {0}, '{1}', {2}", cmd.ToString(), ch.ToString(), (int)ch)); } return(true); #endif } } return(false); }
void UpdateSelection(ArrayList edits) { int lineDelta = 0; int indexDelta = 0; int currentLine = 0; bool updateStart = true; bool updateEnd = true; bool selectionIsEmpty = TextSpanHelper.IsEmpty(this.selection); foreach (EditSpan es in edits) { TextSpan span = es.Span; string text = es.Text; int lastLine = currentLine; int lastDelta = indexDelta; if (currentLine != span.iStartLine) { // We have moved to a new line, so the indexDelta is no longer relevant. currentLine = span.iStartLine; indexDelta = 0; } // Now adjust the span based on the current deltas. span.iStartIndex += indexDelta; if (currentLine == span.iEndLine) { span.iEndIndex += indexDelta; } span.iStartLine += lineDelta; span.iEndLine += lineDelta; if (updateStart) { TextSpan original = es.Span; if (TextSpanHelper.ContainsInclusive(original, this.selection.iStartLine, this.selection.iStartIndex)) { bool atEnd = (this.selection.iStartLine == original.iEndLine && this.selection.iStartIndex == original.iEndIndex); this.selection.iStartLine = span.iStartLine; this.selection.iStartIndex = span.iStartIndex; if (atEnd) { // Selection was positioned at the end of the span, so // skip past the inserted text to approximate that location. if (es.LineCount > 0) { this.selection.iStartLine += es.LineCount; this.selection.iStartIndex = es.LengthOfLastLine; } else { this.selection.iStartIndex += es.LengthOfLastLine; } } updateStart = false; // done } else if (TextSpanHelper.StartsAfterStartOf(original, this.selection)) { if (this.selection.iStartLine == lastLine) { this.selection.iStartIndex += lastDelta; } this.selection.iStartLine += lineDelta; updateStart = false; // done. } if (!updateStart && selectionIsEmpty) { this.selection.iEndLine = this.selection.iStartLine; this.selection.iEndIndex = this.selection.iStartIndex; updateEnd = false; // done } } if (updateEnd) { TextSpan original = es.Span; if (TextSpanHelper.StartsAfterEndOf(original, this.selection)) { if (this.selection.iEndLine == lastLine) { this.selection.iEndIndex += lastDelta; } this.selection.iEndLine += lineDelta; updateEnd = false; // done. } else if (TextSpanHelper.ContainsInclusive(original, this.selection.iEndLine, this.selection.iEndIndex)) { this.selection.iEndLine = span.iStartLine; this.selection.iEndIndex = span.iStartIndex; // Now include the text we are inserting in the selection if (es.LineCount > 0) { this.selection.iEndLine += es.LineCount; this.selection.iEndIndex = es.LengthOfLastLine; } else { this.selection.iEndIndex += es.LengthOfLastLine; } updateEnd = false; // done. } } // Now adjust the deltas based on whether we just deleted anything. if (span.iStartLine != span.iEndLine) { // We are deleting one or more lines. lineDelta += (span.iStartLine - span.iEndLine); indexDelta = -span.iEndIndex; currentLine = span.iStartLine; } else if (span.iStartIndex != span.iEndIndex) { indexDelta += (span.iStartIndex - span.iEndIndex); } // Now adjust the deltas based on what we just inserted if (!string.IsNullOrEmpty(text)) { lineDelta += es.LineCount; if (span.iStartLine != span.iEndLine) // we removed multiple lines { if (es.LineCount == 0) // but we are not inserting any new lines // Then we are appending to this line. { indexDelta = span.iStartIndex + es.LengthOfLastLine; } else { indexDelta = es.LengthOfLastLine; // otherwise we just started a new line. } } else if (es.LineCount != 0) // we inserted new lines // then calculate delta between new position versus position on original line. { indexDelta += es.LengthOfLastLine - span.iStartIndex; } else { indexDelta += es.LengthOfLastLine; // then delta is simply what we just inserted } } } if (updateStart) { // Then start of selection is off the end of the list of edits. if (this.selection.iStartLine == currentLine) { this.selection.iStartIndex += indexDelta; } this.selection.iStartLine += lineDelta; } if (updateEnd) { // Then end of selection is off the end of the list of edits. if (this.selection.iEndLine == currentLine) { this.selection.iEndIndex += indexDelta; } this.selection.iEndLine += lineDelta; } }