зеркало из https://github.com/dotnet/fsharp.git
A naïve telemetry duration implementation (#15219)
This commit is contained in:
Родитель
e267bb9f8d
Коммит
20f646a55c
|
@ -12,7 +12,11 @@ vsintegration/*
|
|||
!vsintegration/tests/FSharp.Editor.Tests
|
||||
artifacts/
|
||||
|
||||
# Explicitly unformatted implementation
|
||||
# For some reason, it tries to format files from remotes (Processing .\.git\refs\remotes\<remote>\FSComp.fsi)
|
||||
.git/
|
||||
|
||||
|
||||
# Explicitly unformatted implementation
|
||||
src/Compiler/Checking/AccessibilityLogic.fs
|
||||
src/Compiler/Checking/AttributeChecking.fs
|
||||
src/Compiler/Checking/AugmentWithHashCompare.fs
|
||||
|
@ -113,4 +117,3 @@ src/FSharp.Core/list.fsi
|
|||
src/FSharp.Core/Query.fsi
|
||||
src/FSharp.Core/resumable.fsi
|
||||
src/FSharp.Core/async.fsi
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Classification
|
|||
open FSharp.Compiler.CodeAnalysis
|
||||
open FSharp.Compiler.EditorServices
|
||||
open FSharp.Compiler.Tokenization
|
||||
open Microsoft.VisualStudio.FSharp.Editor.Telemetry
|
||||
|
||||
// IEditorClassificationService is marked as Obsolete, but is still supported. The replacement (IClassificationService)
|
||||
// is internal to Microsoft.CodeAnalysis.Workspaces which we don't have internals visible to. Rather than add yet another
|
||||
|
@ -183,7 +184,20 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
|
|||
// For closed documents, only get classification for the text within the span.
|
||||
// This may be inaccurate for multi-line tokens such as string literals, but this is ok for now
|
||||
// as it's better than having to tokenize a big part of a file which in return will allocate a lot and hurt find all references performance.
|
||||
if not (document.Project.Solution.Workspace.IsDocumentOpen document.Id) then
|
||||
let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id
|
||||
|
||||
let eventProps: (string * obj) array =
|
||||
[|
|
||||
"context.document.project.id", document.Project.Id.Id.ToString()
|
||||
"context.document.id", document.Id.Id.ToString()
|
||||
"isOpenDocument", isOpenDocument
|
||||
"textSpanLength", textSpan.Length
|
||||
|]
|
||||
|
||||
use _eventDuration =
|
||||
TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticCalssifications, eventProps)
|
||||
|
||||
if not isOpenDocument then
|
||||
result.AddRange(getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken))
|
||||
else
|
||||
result.AddRange(
|
||||
|
@ -200,7 +214,7 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
|
|||
}
|
||||
|> RoslynHelpers.StartAsyncUnitAsTask cancellationToken
|
||||
|
||||
member this.AddSemanticClassificationsAsync
|
||||
member _.AddSemanticClassificationsAsync
|
||||
(
|
||||
document: Document,
|
||||
textSpan: TextSpan,
|
||||
|
@ -215,16 +229,52 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
|
|||
// If we are trying to get semantic classification for a document that is not open, get the results from the background and cache it.
|
||||
// We do this for find all references when it is populating results.
|
||||
// We cache it temporarily so we do not have to continously call into the checker and perform a background operation.
|
||||
if not (document.Project.Solution.Workspace.IsDocumentOpen document.Id) then
|
||||
let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id
|
||||
|
||||
if not isOpenDocument then
|
||||
match! semanticClassificationCache.TryGetValueAsync document with
|
||||
| ValueSome classificationDataLookup ->
|
||||
let eventProps: (string * obj) array =
|
||||
[|
|
||||
"context.document.project.id", document.Project.Id.Id.ToString()
|
||||
"context.document.id", document.Id.Id.ToString()
|
||||
"isOpenDocument", isOpenDocument
|
||||
"textSpanLength", textSpan.Length
|
||||
"cacheHit", true
|
||||
|]
|
||||
|
||||
use _eventDuration =
|
||||
TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps)
|
||||
|
||||
addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result
|
||||
| _ ->
|
||||
let eventProps =
|
||||
[|
|
||||
"isOpenDocument", isOpenDocument :> obj
|
||||
"textSpanLength", textSpan.Length
|
||||
"cacheHit", false
|
||||
|]
|
||||
|
||||
use _eventDuration =
|
||||
TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps)
|
||||
|
||||
let! classificationData = document.GetFSharpSemanticClassificationAsync(nameof (FSharpClassificationService))
|
||||
let classificationDataLookup = toSemanticClassificationLookup classificationData
|
||||
do! semanticClassificationCache.SetAsync(document, classificationDataLookup)
|
||||
addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result
|
||||
else
|
||||
let eventProps: (string * obj) array =
|
||||
[|
|
||||
"context.document.project.id", document.Project.Id.Id.ToString()
|
||||
"context.document.id", document.Id.Id.ToString()
|
||||
"isOpenDocument", isOpenDocument
|
||||
"textSpanLength", textSpan.Length
|
||||
"cacheHit", false
|
||||
|]
|
||||
|
||||
use _eventDuration =
|
||||
TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps)
|
||||
|
||||
let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (IFSharpClassificationService))
|
||||
|
||||
let targetRange =
|
||||
|
|
|
@ -17,21 +17,27 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry
|
|||
|
||||
[<RequireQualifiedAccess>]
|
||||
module internal CodeFixHelpers =
|
||||
let private reportCodeFixTelemetry (diagnostics: ImmutableArray<Diagnostic>) (doc: Document) (staticName: string) (additionalProps) =
|
||||
let private reportCodeFixTelemetry
|
||||
(diagnostics: ImmutableArray<Diagnostic>)
|
||||
(doc: Document)
|
||||
(staticName: string)
|
||||
(additionalProps: (string * obj) array)
|
||||
=
|
||||
let ids =
|
||||
diagnostics |> Seq.map (fun d -> d.Id) |> Seq.distinct |> String.concat ","
|
||||
|
||||
let props: (string * obj) list =
|
||||
additionalProps
|
||||
@ [
|
||||
let defaultProps: (string * obj) array =
|
||||
[|
|
||||
"name", staticName
|
||||
"ids", ids
|
||||
"context.document.project.id", doc.Project.Id.Id.ToString()
|
||||
"context.document.id", doc.Id.Id.ToString()
|
||||
"context.diagnostics.count", diagnostics.Length
|
||||
]
|
||||
|]
|
||||
|
||||
TelemetryReporter.reportEvent "codefixactivated" props
|
||||
let props: (string * obj) array = Array.concat [ additionalProps; defaultProps ]
|
||||
|
||||
TelemetryReporter.ReportSingleEvent(TelemetryEvents.CodefixActivated, props)
|
||||
|
||||
let createFixAllProvider name getChanges =
|
||||
FixAllProvider.Create(fun fixAllCtx doc allDiagnostics ->
|
||||
|
@ -46,7 +52,7 @@ module internal CodeFixHelpers =
|
|||
allDiagnostics
|
||||
doc
|
||||
name
|
||||
[ "scope", fixAllCtx.Scope.ToString(); "elapsedMs", sw.ElapsedMilliseconds ]
|
||||
[| "scope", fixAllCtx.Scope.ToString(); "elapsedMs", sw.ElapsedMilliseconds |]
|
||||
|
||||
return doc
|
||||
})
|
||||
|
@ -58,7 +64,7 @@ module internal CodeFixHelpers =
|
|||
backgroundTask {
|
||||
let! sourceText = context.Document.GetTextAsync(cancellationToken)
|
||||
let doc = context.Document.WithText(sourceText.WithChanges(changes))
|
||||
reportCodeFixTelemetry context.Diagnostics context.Document name []
|
||||
reportCodeFixTelemetry context.Diagnostics context.Document name [||]
|
||||
return doc
|
||||
}),
|
||||
name
|
||||
|
|
|
@ -13,6 +13,7 @@ open Microsoft.CodeAnalysis.Completion
|
|||
open Microsoft.CodeAnalysis.Text
|
||||
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Completion
|
||||
|
||||
open Microsoft.VisualStudio.FSharp.Editor.Telemetry
|
||||
open Microsoft.VisualStudio.Shell
|
||||
|
||||
open FSharp.Compiler.CodeAnalysis
|
||||
|
@ -142,6 +143,7 @@ type internal FSharpCompletionProvider
|
|||
) =
|
||||
|
||||
asyncMaybe {
|
||||
|
||||
let! parseResults, checkFileResults =
|
||||
document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux")
|
||||
|> liftAsync
|
||||
|
@ -298,6 +300,16 @@ type internal FSharpCompletionProvider
|
|||
Logger.LogBlockMessage context.Document.Name LogEditorFunctionId.Completion_ProvideCompletionsAsync
|
||||
|
||||
let document = context.Document
|
||||
|
||||
let eventProps: (string * obj) array =
|
||||
[|
|
||||
"context.document.project.id", document.Project.Id.Id.ToString()
|
||||
"context.document.id", document.Id.Id.ToString()
|
||||
|]
|
||||
|
||||
use _eventDuration =
|
||||
TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.ProvideCompletions, eventProps)
|
||||
|
||||
let! sourceText = context.Document.GetTextAsync(context.CancellationToken)
|
||||
let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion()
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics
|
|||
|
||||
open FSharp.Compiler.CodeAnalysis
|
||||
open FSharp.Compiler.Diagnostics
|
||||
open Microsoft.VisualStudio.FSharp.Editor.Telemetry
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
type internal DiagnosticsType =
|
||||
|
@ -53,6 +54,20 @@ type internal FSharpDocumentDiagnosticAnalyzer [<ImportingConstructor>] () =
|
|||
|
||||
static member GetDiagnostics(document: Document, diagnosticType: DiagnosticsType) =
|
||||
async {
|
||||
|
||||
let eventProps: (string * obj) array =
|
||||
[|
|
||||
"context.document.project.id", document.Project.Id.Id.ToString()
|
||||
"context.document.id", document.Id.Id.ToString()
|
||||
"context.diagnostics.type",
|
||||
match diagnosticType with
|
||||
| DiagnosticsType.Syntax -> "syntax"
|
||||
| DiagnosticsType.Semantic -> "semantic"
|
||||
|]
|
||||
|
||||
use _eventDuration =
|
||||
TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GetDiagnosticsForDocument, eventProps)
|
||||
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let! parseResults = document.GetFSharpParseResultsAsync("GetDiagnostics")
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
</EmbeddedText>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)..\..\..\src\LegacyMSBuildResolver\LegacyMSBuildReferenceResolver.fsi" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)..\..\..\src\LegacyMSBuildResolver\LegacyMSBuildReferenceResolver.fs" />
|
||||
<Compile Include="Telemetry\TelemetryReporter.fs" />
|
||||
<Compile Include="Common\AssemblyInfo.fs" />
|
||||
<Compile Include="Common\Logger.fsi" />
|
||||
<Compile Include="Common\Logger.fs" />
|
||||
|
@ -44,6 +43,7 @@
|
|||
<Compile Include="Options\SettingsStore.fs" />
|
||||
<Compile Include="Options\UIHelpers.fs" />
|
||||
<Compile Include="Options\EditorOptions.fs" />
|
||||
<Compile Include="Telemetry\TelemetryReporter.fs" />
|
||||
<Compile Include="LanguageService\ProvideBraceCompletionAttribute.fs" />
|
||||
<Compile Include="LanguageService\FSharpEditorFactory.fs" />
|
||||
<Compile Include="LanguageService\TextViewCreationListener.fs" />
|
||||
|
|
|
@ -27,7 +27,7 @@ type internal RoslynAdapter [<ImportingConstructor>] (settings: EditorOptions) =
|
|||
return ImmutableArray.Empty
|
||||
else
|
||||
let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat ","
|
||||
TelemetryReporter.reportEvent "hints" [ ("hints.kinds", hintKindsSerialized) ]
|
||||
TelemetryReporter.ReportSingleEvent(TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized) |])
|
||||
|
||||
let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask
|
||||
let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName cancellationToken
|
||||
|
|
|
@ -118,7 +118,7 @@ type internal FSharpWorkspaceServiceFactory [<System.Composition.ImportingConstr
|
|||
| _ ->
|
||||
let checker =
|
||||
lazy
|
||||
TelemetryReporter.reportEvent "languageservicestarted" []
|
||||
TelemetryReporter.ReportSingleEvent(TelemetryEvents.LanguageServiceStarted, [||])
|
||||
|
||||
let editorOptions = workspace.Services.GetService<EditorOptions>()
|
||||
|
||||
|
@ -162,9 +162,9 @@ type internal FSharpWorkspaceServiceFactory [<System.Composition.ImportingConstr
|
|||
useSyntaxTreeCache = useSyntaxTreeCache
|
||||
)
|
||||
|
||||
TelemetryReporter.reportEvent
|
||||
"languageservicestarted"
|
||||
[
|
||||
TelemetryReporter.ReportSingleEvent(
|
||||
TelemetryEvents.LanguageServiceStarted,
|
||||
[|
|
||||
nameof enableLiveBuffers, enableLiveBuffers
|
||||
nameof useSyntaxTreeCache, useSyntaxTreeCache
|
||||
nameof enableParallelReferenceResolution, enableParallelReferenceResolution
|
||||
|
@ -173,7 +173,8 @@ type internal FSharpWorkspaceServiceFactory [<System.Composition.ImportingConstr
|
|||
nameof isInlineTypeHintsEnabled, isInlineTypeHintsEnabled
|
||||
nameof isInlineReturnTypeHintsEnabled, isInlineReturnTypeHintsEnabled
|
||||
nameof enablePartialTypeChecking, enablePartialTypeChecking
|
||||
]
|
||||
|]
|
||||
)
|
||||
|
||||
if enableLiveBuffers then
|
||||
workspace.WorkspaceChanged.Add(fun args ->
|
||||
|
|
|
@ -72,11 +72,13 @@ module internal SymbolHelpers =
|
|||
| firstProject :: _ ->
|
||||
let isFastFindReferencesEnabled = firstProject.IsFastFindReferencesEnabled
|
||||
|
||||
// TODO: this needs to use already boxed boolean instead of boxing it every time.
|
||||
let props =
|
||||
[ nameof isFastFindReferencesEnabled, isFastFindReferencesEnabled :> obj ]
|
||||
[| nameof isFastFindReferencesEnabled, isFastFindReferencesEnabled :> obj |]
|
||||
|
||||
backgroundTask {
|
||||
TelemetryReporter.reportEvent "getSymbolUsesInProjectsStarted" props
|
||||
// TODO: this needs to be a single event with a duration
|
||||
TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsStarted, props)
|
||||
|
||||
do!
|
||||
projects
|
||||
|
@ -84,7 +86,7 @@ module internal SymbolHelpers =
|
|||
Task.Run(fun () -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects", ct)))
|
||||
|> Task.WhenAll
|
||||
|
||||
TelemetryReporter.reportEvent "getSymbolUsesInProjectsFinished" props
|
||||
TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsFinished, props)
|
||||
}
|
||||
|
||||
let findSymbolUses (symbolUse: FSharpSymbolUse) (currentDocument: Document) (checkFileResults: FSharpCheckFileResults) onFound =
|
||||
|
|
|
@ -105,6 +105,7 @@ type AdvancedOptions =
|
|||
IsInlineParameterNameHintsEnabled: bool
|
||||
IsInlineReturnTypeHintsEnabled: bool
|
||||
IsLiveBuffersEnabled: bool
|
||||
SendAdditionalTelemetry: bool
|
||||
}
|
||||
|
||||
static member Default =
|
||||
|
@ -115,6 +116,7 @@ type AdvancedOptions =
|
|||
IsInlineParameterNameHintsEnabled = false
|
||||
IsInlineReturnTypeHintsEnabled = false
|
||||
IsLiveBuffersEnabled = FSharpExperimentalFeaturesEnabledAutomatically
|
||||
SendAdditionalTelemetry = FSharpExperimentalFeaturesEnabledAutomatically
|
||||
}
|
||||
|
||||
[<CLIMutable>]
|
||||
|
|
|
@ -3,25 +3,144 @@
|
|||
namespace Microsoft.VisualStudio.FSharp.Editor.Telemetry
|
||||
|
||||
open Microsoft.VisualStudio.Telemetry
|
||||
open System
|
||||
open System.Diagnostics
|
||||
open System.Collections.Concurrent
|
||||
open Microsoft.VisualStudio.Shell
|
||||
open Microsoft.VisualStudio
|
||||
open Microsoft.VisualStudio.LanguageServices
|
||||
open Microsoft.VisualStudio.FSharp.Editor
|
||||
|
||||
#nowarn "3220" // Ignore warning about direct tuple items access.
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TelemetryEvents =
|
||||
[<Literal>]
|
||||
let CodefixActivated = "codefixactivated"
|
||||
|
||||
[<Literal>]
|
||||
let Hints = "hints"
|
||||
|
||||
[<Literal>]
|
||||
let LanguageServiceStarted = "languageservicestarted"
|
||||
|
||||
[<Literal>]
|
||||
let GetSymbolUsesInProjectsStarted = "getSymbolUsesInProjectsStarted"
|
||||
|
||||
[<Literal>]
|
||||
let GetSymbolUsesInProjectsFinished = "getSymbolUsesInProjectsFinished"
|
||||
|
||||
[<Literal>]
|
||||
let AddSyntacticCalssifications = "addsyntacticclassifications"
|
||||
|
||||
[<Literal>]
|
||||
let AddSemanticCalssifications = "addsemanticclassifications"
|
||||
|
||||
[<Literal>]
|
||||
let GetDiagnosticsForDocument = "getdiagnosticsfordocument"
|
||||
|
||||
[<Literal>]
|
||||
let ProvideCompletions = "providecompletions"
|
||||
|
||||
// TODO: needs to be something more sophisticated in future
|
||||
[<Struct; RequireQualifiedAccess; NoComparison; NoEquality>]
|
||||
type TelemetryThrottlingStrategy =
|
||||
| NoThrottling
|
||||
| Throttle of {| Timeout: TimeSpan |}
|
||||
// At most, send one event per 5 seconds.
|
||||
static member Default =
|
||||
Throttle
|
||||
{|
|
||||
Timeout = TimeSpan.FromSeconds(5.0)
|
||||
|}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TelemetryReporter =
|
||||
let internal noopDisposable =
|
||||
{ new IDisposable with
|
||||
member _.Dispose() = ()
|
||||
}
|
||||
|
||||
let private eventPrefix = "dotnet/fsharp/"
|
||||
let private propPrefix = "dotnet.fsharp."
|
||||
let internal lastTriggeredEvents = ConcurrentDictionary<string, DateTime>()
|
||||
|
||||
let private getFullEventName name = eventPrefix + name
|
||||
let private getFullPropName name = propPrefix + name
|
||||
[<Literal>]
|
||||
let eventPrefix = "dotnet/fsharp/"
|
||||
|
||||
let private createEvent name (props: (string * obj) list) =
|
||||
let event = TelemetryEvent(getFullEventName name)
|
||||
[<Literal>]
|
||||
let propPrefix = "dotnet.fsharp."
|
||||
|
||||
props
|
||||
|> List.map (fun (k, v) -> getFullPropName k, v)
|
||||
|> List.iter event.Properties.Add
|
||||
// This should always be inlined.
|
||||
let inline createEvent name (props: (string * obj) array) =
|
||||
let eventName = eventPrefix + name
|
||||
let event = TelemetryEvent(eventName, TelemetrySeverity.Normal)
|
||||
|
||||
// TODO:
|
||||
// We need to utilize TelemetryEvent's Correlation id, so we can track (for example) events on one document in the project.
|
||||
|
||||
// TODO: need to carefully review the code, since it will be a hot path when we are sending telemetry
|
||||
// This particular approach is here to avoid alocations for properties, which is likely the case if we destructing them.
|
||||
for prop in props do
|
||||
event.Properties.Add(propPrefix + prop.Item1, prop.Item2)
|
||||
|
||||
event
|
||||
|
||||
let reportEvent name props =
|
||||
let session = TelemetryService.DefaultSession
|
||||
let event = createEvent name props
|
||||
session.PostEvent event
|
||||
[<Struct; NoComparison; NoEquality>]
|
||||
type TelemetryReporter private (name: string, props: (string * obj) array, stopwatch: Stopwatch) =
|
||||
|
||||
static member val private SendAdditionalTelemetry =
|
||||
lazy
|
||||
(let componentModel =
|
||||
Package.GetGlobalService(typeof<ComponentModelHost.SComponentModel>) :?> ComponentModelHost.IComponentModel
|
||||
|
||||
if componentModel = null then
|
||||
TelemetryService.DefaultSession.IsUserMicrosoftInternal
|
||||
else
|
||||
let workspace = componentModel.GetService<VisualStudioWorkspace>()
|
||||
workspace.Services.GetService<EditorOptions>().Advanced.SendAdditionalTelemetry)
|
||||
|
||||
static member ReportSingleEvent(name, props) =
|
||||
let event = TelemetryReporter.createEvent name props
|
||||
TelemetryService.DefaultSession.PostEvent event
|
||||
|
||||
// A naïve implementation using stopwatch and returning an IDisposable
|
||||
// TODO: needs a careful review, since it will be a hot path when we are sending telemetry
|
||||
static member ReportSingleEventWithDuration(name, props, ?throttlingStrategy) : IDisposable =
|
||||
|
||||
let additionalTelemetryEnabled = not TelemetryReporter.SendAdditionalTelemetry.Value
|
||||
|
||||
let isUserMicrosoftInternal =
|
||||
TelemetryService.DefaultSession.IsUserMicrosoftInternal
|
||||
|
||||
if additionalTelemetryEnabled || isUserMicrosoftInternal then
|
||||
let throttlingStrategy =
|
||||
defaultArg throttlingStrategy TelemetryThrottlingStrategy.Default
|
||||
|
||||
match throttlingStrategy with
|
||||
| TelemetryThrottlingStrategy.NoThrottling ->
|
||||
let stopwatch = Stopwatch()
|
||||
stopwatch.Start()
|
||||
new TelemetryReporter(name, props, stopwatch)
|
||||
| TelemetryThrottlingStrategy.Throttle s ->
|
||||
// This is not "atomic" for now, theoretically multiple threads can send the event as for now.
|
||||
match TelemetryReporter.lastTriggeredEvents.TryGetValue(name) with
|
||||
| false, lastTriggered
|
||||
| true, lastTriggered when lastTriggered + s.Timeout < DateTime.UtcNow ->
|
||||
let stopwatch = Stopwatch()
|
||||
stopwatch.Start()
|
||||
// Whenever we create an event, we update the time.
|
||||
TelemetryReporter.lastTriggeredEvents.AddOrUpdate(name, DateTime.UtcNow, (fun _ _ -> DateTime.UtcNow))
|
||||
|> ignore
|
||||
|
||||
new TelemetryReporter(name, props, stopwatch)
|
||||
| _ -> TelemetryReporter.noopDisposable
|
||||
else
|
||||
TelemetryReporter.noopDisposable
|
||||
|
||||
interface IDisposable with
|
||||
member _.Dispose() =
|
||||
stopwatch.Stop()
|
||||
|
||||
let event =
|
||||
TelemetryReporter.createEvent name (Array.concat [ props; [| "vs_event_duration_ms", stopwatch.ElapsedMilliseconds |] ])
|
||||
|
||||
TelemetryService.DefaultSession.PostEvent event
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
<CheckBox x:Name="toggleLiveBuffers" IsChecked="{Binding IsLiveBuffersEnabled}"
|
||||
Content="{x:Static local:Strings.Enable_Live_Buffers}"/>
|
||||
</GroupBox>
|
||||
<GroupBox Header="{x:Static local:Strings.AdditionalTelemetry}">
|
||||
<CheckBox x:Name="toggleSendAdditionalTelemetry" IsChecked="{Binding SendAdditionalTelemetry}"
|
||||
Content="{x:Static local:Strings.Send_Additional_Telemetry}"/>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
|
|
@ -60,6 +60,15 @@ namespace Microsoft.VisualStudio.FSharp.UIResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Additional performance telemetry (experimental).
|
||||
/// </summary>
|
||||
public static string AdditionalTelemetry {
|
||||
get {
|
||||
return ResourceManager.GetString("AdditionalTelemetry", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Always place open statements at the top level.
|
||||
/// </summary>
|
||||
|
@ -312,6 +321,15 @@ namespace Microsoft.VisualStudio.FSharp.UIResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Send additional performance telemetry.
|
||||
/// </summary>
|
||||
public static string Send_Additional_Telemetry {
|
||||
get {
|
||||
return ResourceManager.GetString("Send_Additional_Telemetry", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show s_ymbols in unopened namespaces.
|
||||
/// </summary>
|
||||
|
|
|
@ -237,6 +237,12 @@
|
|||
<data name="LiveBuffers" xml:space="preserve">
|
||||
<value>Live Buffers (experimental)</value>
|
||||
</data>
|
||||
<data name="AdditionalTelemetry" xml:space="preserve">
|
||||
<value>Additional performance telemetry (experimental)</value>
|
||||
</data>
|
||||
<data name="Send_Additional_Telemetry" xml:space="preserve">
|
||||
<value>Send additional performance telemetry</value>
|
||||
</data>
|
||||
<data name="Use_syntax_tree_cache" xml:space="preserve">
|
||||
<value>Cache parsing results (experimental)</value>
|
||||
</data>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="cs" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Vždy umístit otevřené příkazy na nejvyšší úroveň</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Upřednostňovaná šířka popisu ve znacích</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Zobrazit nápovědy k názvům vložených parametrů (preview)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="de" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">open-Anweisungen immer an oberster Ebene platzieren</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Bevorzugte Beschreibungsbreite in Zeichen</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Hinweise zu Inlineparameternamen anzeigen (Vorschau)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="es" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Colocar siempre las instrucciones open en el nivel superior</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Anchura preferida de la descripción en caracteres</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Mostrar sugerencias de nombres de parámetros en línea (vista previa)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="fr" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Placer toujours les instructions open au niveau supérieur</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Largeur de description préférée en caractères</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Afficher les conseils de nom de paramètre en ligne (préversion)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="it" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Inserisci sempre le istruzioni OPEN al primo livello</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Larghezza descrizione preferita in caratteri</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Visualizza suggerimenti per i nomi di parametro inline (anteprima)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="ja" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Open ステートメントを常に最上位に配置する</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">優先する説明の文字幅</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">インライン パラメーター名のヒントを表示する (プレビュー)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="ko" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">항상 최상위에 open 문 배치</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">기본 설정 설명 너비(문자)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">인라인 매개 변수 이름 힌트 표시(미리 보기)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="pl" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Zawsze umieszczaj otwarte instrukcje na najwyższym poziomie</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Preferowana szerokość opisu w znakach</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Wyświetl wskazówki w tekście dotyczące nazw parametrów (wersja zapoznawcza)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="pt-BR" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Sempre coloque as instruções abertas no nível superior</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Largura de descrição preferencial em caracteres</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Exibir dicas de nome de parâmetro embutido (versão prévia)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="ru" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Всегда располагайте открытые операторы на верхнем уровне</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Предпочитаемая ширина описания в символах</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Отображение подсказок для имен встроенных параметров (предварительная версия)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="tr" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">Açık deyimleri her zaman en üst düzeye yerleştir</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">Karakter olarak tercih edilen açıklama genişliği</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">Satır içi parametre adı ipuçlarını görüntüle (önizleme)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="zh-Hans" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">始终在顶层放置 open 语句</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">以字符为单位的首选说明宽度</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">显示内联参数名称提示(预览版)</target>
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" source-language="en" target-language="zh-Hant" original="../Strings.resx">
|
||||
<body>
|
||||
<trans-unit id="AdditionalTelemetry">
|
||||
<source>Additional performance telemetry (experimental)</source>
|
||||
<target state="new">Additional performance telemetry (experimental)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Always_place_opens_at_top_level">
|
||||
<source>Always place open statements at the top level</source>
|
||||
<target state="translated">一律將 open 陳述式放在最上層</target>
|
||||
|
@ -92,6 +97,11 @@
|
|||
<target state="translated">慣用説明寬度 (以字元為單位)</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Send_Additional_Telemetry">
|
||||
<source>Send additional performance telemetry</source>
|
||||
<target state="new">Send additional performance telemetry</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Show_Inline_Parameter_Name_Hints">
|
||||
<source>Display inline parameter name hints (preview)</source>
|
||||
<target state="translated">顯示內嵌參數名稱提示 (預覽)</target>
|
||||
|
|
Загрузка…
Ссылка в новой задаче