/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt VS Tools. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ using System; using System.ComponentModel.Composition; using System.Linq; using Microsoft.VisualStudio; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities; using QtVsTools.Qml.Syntax; using QtVsTools.VisualStudio; namespace QtVsTools.Qml.Classification { [Export(typeof(IViewTaggerProvider))] [ContentType("qml")] [TagType(typeof(ClassificationTag))] internal sealed class QmlExpressionEvalProvider : IViewTaggerProvider { public const string ClassificationType = QmlExpressionEval.ClassificationType; [Import] internal IClassificationTypeRegistryService classificationTypeRegistry = null; [Export(typeof(ClassificationTypeDefinition))] [Name(ClassificationType)] internal static ClassificationTypeDefinition qmlDebug = null; public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where T : ITag { QmlClassificationType.InitClassificationTypes(classificationTypeRegistry); if (!QmlClassificationType.ClassificationTypes.ContainsKey(ClassificationType)) { QmlClassificationType.ClassificationTypes.Add(ClassificationType, classificationTypeRegistry.GetClassificationType(ClassificationType)); } return QmlExpressionEval.Create(textView, buffer) as ITagger; } } internal sealed class QmlExpressionEval : QmlAsyncClassifier, IOleCommandTarget, IVsTextViewFilter { public const string ClassificationType = "expr_eval.qml"; ITextView textView; ITextBuffer buffer; IVsDebugger debugger; IVsTextView vsTextView; IVsTextLines textLines; IOleCommandTarget nextTarget; public static QmlExpressionEval Create(ITextView textView, ITextBuffer buffer) { var _this = new QmlExpressionEval(textView, buffer); return _this.Initialize(textView, buffer) ? _this : null; } private QmlExpressionEval(ITextView textView, ITextBuffer buffer) : base(ClassificationType, textView, buffer) { } private bool Initialize(ITextView textView, ITextBuffer buffer) { this.textView = textView; this.buffer = buffer; debugger = VsServiceProvider.GetService(); if (debugger == null) return false; var componentModel = VsServiceProvider .GetService(); if (componentModel == null) return false; var editorFactory = componentModel.GetService(); if (editorFactory == null) return false; vsTextView = editorFactory.GetViewAdapter(textView); if (vsTextView == null) return false; if (vsTextView.GetBuffer(out textLines) != VSConstants.S_OK) return false; if (vsTextView.AddCommandFilter(this, out nextTarget) != VSConstants.S_OK) return false; textView.Closed += TextView_Closed; return true; } private void TextView_Closed(object sender, EventArgs e) { vsTextView.RemoveCommandFilter(this); } protected override ClassificationRefresh ProcessText( ITextSnapshot snapshot, Parser parseResult, SharedTagList tagList, bool writeAccess) { if (writeAccess) { var expressions = parseResult.AstNodes .Where(x => x.Kind == AstNodeKind.FieldMemberExpression || x.Kind == AstNodeKind.IdentifierExpression) .GroupBy(x => x.FirstSourceLocation.Offset) .Select(x => new { Offset = x.Key, Length = x.Max(y => y.LastSourceLocation.Offset + y.LastSourceLocation.Length) - x.Key, List = x.OrderBy(y => y.LastSourceLocation.Offset + y.LastSourceLocation.Length) }); tagList.AddRange(this, expressions .Select(x => new ExprTrackingTag(snapshot, x.Offset, x.Length, x.List))); } return ClassificationRefresh.FullText; } protected override ClassificationTag GetClassification(TrackingTag tag) { var debugTag = tag as ExprTrackingTag; if (debugTag == null) return null; return new ExprTag(debugTag.Exprs, QmlClassificationType.Get(ClassificationType)); } int IVsTextViewFilter.GetDataTipText(TextSpan[] pSpan, out string pbstrText) { pbstrText = ""; var dbgMode = new DBGMODE[1]; if (debugger.GetMode(dbgMode) != VSConstants.S_OK || dbgMode[0] != DBGMODE.DBGMODE_Break) { return VSConstants.S_FALSE; } var startLine = buffer.CurrentSnapshot.GetLineFromLineNumber(pSpan[0].iStartLine); var offset = startLine.Start.Position + pSpan[0].iStartIndex; var spans = new NormalizedSnapshotSpanCollection( new SnapshotSpan(buffer.CurrentSnapshot, offset, 1)); var tags = GetTags(spans).Select(x => x.Tag).Cast(); var expr = tags.SelectMany(x => x.Exprs) .Where(x => offset < x.LastSourceLocation.Offset + x.LastSourceLocation.Length) .FirstOrDefault(); if (expr == null) return VSConstants.S_FALSE; var exprSpan = new Span(expr.FirstSourceLocation.Offset, expr.LastSourceLocation.Offset + expr.LastSourceLocation.Length - expr.FirstSourceLocation.Offset); var exprText = buffer.CurrentSnapshot.GetText(exprSpan); return debugger.GetDataTipValue(textLines, pSpan, exprText, out pbstrText); } int IVsTextViewFilter.GetWordExtent(int iLine, int iIndex, uint dwFlags, TextSpan[] pSpan) { return VSConstants.E_NOTIMPL; } int IVsTextViewFilter.GetPairExtents(int iLine, int iIndex, TextSpan[] pSpan) { return VSConstants.E_NOTIMPL; } int IOleCommandTarget.QueryStatus( ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { return nextTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); } int IOleCommandTarget.Exec( ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { return nextTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); } class ExprTrackingTag : TrackingTag { public IOrderedEnumerable Exprs { get; private set; } public ExprTrackingTag( ITextSnapshot snapshot, int offset, int length, IOrderedEnumerable exprs) : base(snapshot, offset, length) { Exprs = exprs; } } class ExprTag : ClassificationTag { public IOrderedEnumerable Exprs { get; private set; } public ExprTag(IOrderedEnumerable exprs, IClassificationType type) : base(type) { Exprs = exprs; } } } }