diff --git a/build.sbt b/build.sbt index 3827f039987..5fc27348277 100644 --- a/build.sbt +++ b/build.sbt @@ -138,7 +138,7 @@ commands ++= Seq( runMtagsPublishLocal(st, v, localSnapshotVersion) } "interfaces/publishLocal" :: - "telemetryInterfaces/publishLocal" :: + "+telemetryInterfaces/publishLocal" :: s"++${V.scala213} metals/publishLocal" :: "mtags-java/publishLocal" :: publishMtags @@ -193,8 +193,6 @@ def lintingOptions(scalaVersion: String) = { } val sharedJavacOptions = List( - packageDoc / publishArtifact := true, - packageSrc / publishArtifact := true, Compile / javacOptions ++= { if (sys.props("java.version").startsWith("1.8")) Nil @@ -257,13 +255,20 @@ lazy val interfaces = project lazy val telemetryInterfaces = project .in(file("telemetry-interfaces")) - .settings(sharedJavacOptions) + .settings(sharedSettings) .settings( moduleName := "telemetry-interfaces", - autoScalaLibrary := false, - crossVersion := CrossVersion.disabled, - crossPaths := false, - libraryDependencies += "com.google.code.gson" % "gson" % V.gson, + crossScalaVersions := List(V.scala213, V.scala3), + crossVersion := CrossVersion.binary, + Compile / scalacOptions ++= { + if (scalaVersion.value == V.scala3) + List("-Xmax-inlines", "64") + else Nil + }, + libraryDependencies := List( + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.27.7", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.27.7" % "compile-internal" + ) ) lazy val mtagsShared = project @@ -697,7 +702,6 @@ def publishAllMtags( def publishBinaryMtags = (interfaces / publishLocal) .dependsOn( - telemetryInterfaces / publishLocal, `mtags-java` / publishLocal, publishAllMtags(V.quickPublishScalaVersions), ) diff --git a/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala b/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala index 85d7c09384b..4e93cad85b7 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala @@ -1144,10 +1144,10 @@ class Compilers( config: PresentationCompilerConfig, options: List[String], ): ReporterContext = - new ScalaPresentationCompilerContext( - scalaVersion, - options.asJava, - telemetry.conversion.PresentationCompilerConfig(config), + ScalaPresentationCompilerContext( + scalaVersion = scalaVersion, + options = options, + config = telemetry.conversion.PresentationCompilerConfig(config), ) private def remoteReporting( diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala index 05bf5acce04..1400ed524c1 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala @@ -2851,19 +2851,15 @@ class MetalsLspService( def runDoctorCheck(): Unit = doctor.check(headDoctor) private def createTelemetryReporterContext(): telemetry.ReporterContext = - new telemetry.MetalsLspContext( - /* metalsVersion = */ BuildInfo.metalsVersion, - /* userConfig = */ telemetry.conversion.UserConfiguration(userConfig), - /* serverConfig = */ telemetry.conversion.MetalsServerConfig( + telemetry.MetalsLspContext( + metalsVersion = BuildInfo.metalsVersion, + userConfig = telemetry.conversion.UserConfiguration(userConfig), + serverConfig = telemetry.conversion.MetalsServerConfig( serverInputs.initialServerConfig ), - /* clientInfo =*/ telemetry.conversion.MetalsClientInfo( - initializeParams.getClientInfo() - ), - /* buildServerConnections = */ bspSession.toList - .flatMap( - telemetry.conversion.BuildServerConnections(_) - ) - .asJava, + clientInfo = + telemetry.conversion.MetalsClientInfo(initializeParams.getClientInfo()), + buildServerConnections = bspSession.toList + .flatMap(telemetry.conversion.BuildServerConnections(_)), ) } diff --git a/metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala b/metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala index ad993806fa9..52084f306af 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala @@ -242,8 +242,8 @@ object UserConfiguration { "false", "false", "Should display implicit parameter at usage sites", - """|When this option is enabled, each method that has implicit arguments has them - |displayed either as additional decorations if they are supported by the editor or + """|When this option is enabled, each method that has implicit arguments has them + |displayed either as additional decorations if they are supported by the editor or |shown in the hover. |""".stripMargin, ), @@ -252,8 +252,8 @@ object UserConfiguration { "false", "false", "Should display implicit conversion at usage sites", - """|When this option is enabled, each place where an implicit method or class is used has it - |displayed either as additional decorations if they are supported by the editor or + """|When this option is enabled, each place where an implicit method or class is used has it + |displayed either as additional decorations if they are supported by the editor or |shown in the hover. |""".stripMargin, ), @@ -280,7 +280,7 @@ object UserConfiguration { BuildInfo.scala3, BuildInfo.scala3, "Default fallback Scala version", - """|The Scala compiler version that is used as the default or fallback in case a file + """|The Scala compiler version that is used as the default or fallback in case a file |doesn't belong to any build target or the specified Scala version isn't supported by Metals. |This applies to standalone Scala files, worksheets, and Ammonite scripts. """.stripMargin, @@ -343,14 +343,14 @@ object UserConfiguration { ), UserConfigurationOption( "telemetry-level", - TelemetryLevel.default.stringValue, - TelemetryLevel.All.stringValue, + TelemetryLevel.default.textValue, + TelemetryLevel.Anonymous.textValue, "Scope of reported telemetry data", s"""Control what kind of telemetry events can be send to maintainers of Metals. - |With `${TelemetryLevel.Off.stringValue}` no telemetry data would be send. - |Minimal recommended level is `${TelemetryLevel.Error.stringValue}` which would collect diagnostic information when Metals components would crash or fail unexpectedly, allowing to understand why the problem occoured. - |The highest telemtetry level `${TelemetryLevel.All.stringValue}` allows to collect all information including how features are used to help us priortize future improvements." - |Defaults to `${TelemetryLevel.default.stringValue}`. + |With `${TelemetryLevel.Off.textValue}` no telemetry data would be send. + |Minimal recommended level is `${TelemetryLevel.Anonymous.textValue}` which would collect diagnostic information when Metals components would crash or fail unexpectedly, allowing to understand why the problem occoured. + |The highest telemtetry level `${TelemetryLevel.Full.textValue}` allows to collect all information including how features are used to help us priortize future improvements." + |Defaults to `${TelemetryLevel.default.textValue}`. |""".stripMargin, ), ) @@ -568,8 +568,8 @@ object UserConfiguration { getBooleanKey("verbose-compilation").getOrElse(false) val telemetryLevel = getStringKey("telemetry-level") - .flatMap(TelemetryLevel.fromString) - .getOrElse(TelemetryLevel.discover) + .map(TelemetryLevel.fromString) + .getOrElse(TelemetryLevel.default) if (errors.isEmpty) { Right( diff --git a/metals/src/main/scala/scala/meta/internal/telemetry/TelemetryClient.scala b/metals/src/main/scala/scala/meta/internal/telemetry/TelemetryClient.scala index 423142d179b..1ece135d45b 100644 --- a/metals/src/main/scala/scala/meta/internal/telemetry/TelemetryClient.scala +++ b/metals/src/main/scala/scala/meta/internal/telemetry/TelemetryClient.scala @@ -1,48 +1,45 @@ package scala.meta.internal.telemetry -import java.io.InputStreamReader - +import scala.concurrent.ExecutionContext +import scala.concurrent.Future import scala.util.Random -import scala.util.Try -import scala.util.control.NonFatal +import scala.util.Success import scala.meta.internal.metals.LoggerAccess import scala.meta.internal.metals.TelemetryLevel import scala.meta.internal.telemetry -import com.google.gson.JsonSyntaxException import requests.Response object TelemetryClient { - private class DeserializationException(message: String) - extends RuntimeException(message) case class Config(serverHost: String) object Config { // private final val DefaultTelemetryEndpoint = // "https://scala3.westeurope.cloudapp.azure.com/telemetry" - private final val DefaultTelemetryEndpoint = - "http://localhost:8081" - + private final val DefaultTelemetryEndpoint = "http://localhost:8081" val default: Config = Config(DefaultTelemetryEndpoint) } - private class Endpoint[-In, +Out]( - endpoint: telemetry.ServiceEndpoint[In, Out] - )(implicit config: Config) { - def apply(data: In): Try[Out] = { - val json = encodeRequest(data) - execute(json).map(decodeResponse) + private class TelemetryRequest[In]( + endpoint: telemetry.FireAndForgetEndpoint[In], + logger: LoggerAccess, + )(implicit config: Config, ec: ExecutionContext) { + private val endpointURL = s"${config.serverHost}${endpoint.uri}" + private val requester = requests.send(endpoint.method) + println(s"TelemetryClient: sending $endpointURL") + + def apply(data: In): Unit = { + val json = endpoint.encodeInput(data) + val response = execute(json) + acknowledgeResponse(response) } - private val endpointURL = s"${config.serverHost}${endpoint.getUri()}" - private val requester = requests.send(endpoint.getMethod()) - private def execute( data: String, retries: Int = 3, backoffMillis: Int = 100, - ): Try[Response] = Try { + ): Future[Response] = Future { requester( url = endpointURL, data = data, @@ -56,29 +53,14 @@ object TelemetryClient { execute(data, retries - 1, backoffMillis + Random.nextInt(1000)) } - private def encodeRequest(request: In): String = - telemetry.GsonCodecs.gson.toJson(request) - - private def decodeResponse(response: Response): Out = { - if (response.is2xx) { - val outputType = endpoint.getOutputType() - if (outputType == classOf[Void]) ().asInstanceOf[Out] - else { - response.readBytesThrough { is => - try - telemetry.GsonCodecs.gson - .fromJson(new InputStreamReader(is), outputType) - catch { - case err: JsonSyntaxException => - throw new DeserializationException(err.getMessage()) - } - } - } - } else - throw new IllegalStateException( - s"${endpoint.getMethod()}:${endpoint.getUri()} should never result in error, got ${response}" - ) - } + private def acknowledgeResponse(response: Future[Response]): Unit = + response.onComplete { + case Success(value) if value.is2xx => + case _ => + logger.debug( + s"${endpoint.method}:${endpoint.uri} should never result in error, got ${response}" + ) + } } } @@ -86,32 +68,22 @@ private[meta] class TelemetryClient( telemetryLevel: () => TelemetryLevel, config: TelemetryClient.Config = TelemetryClient.Config.default, logger: LoggerAccess = LoggerAccess.system, -) extends telemetry.TelemetryService { - import telemetry.{TelemetryService => api} +)(implicit ec: ExecutionContext) + extends telemetry.TelemetryService { import TelemetryClient._ + import telemetry.TelemetryService._ implicit private def clientConfig: Config = config - private val SendErrorReport = new Endpoint(api.SendErrorReportEndpoint) - private val SendCrashReport = new Endpoint(api.SendCrashReportEndpoint) - - override def sendErrorReport(report: telemetry.ErrorReport): Unit = - if (telemetryLevel().reportErrors) { - SendErrorReport(report) - .recover { case NonFatal(err) => - logSendFailure(reportType = "error")(err) - } - } + private val sendErrorReport0 = + new TelemetryRequest(sendErrorReportEndpoint, logger) + private val sendCrashReport0 = + new TelemetryRequest(sendCrashReportEndpoint, logger) - override def sendCrashReport(report: telemetry.CrashReport): Unit = - if (telemetryLevel().reportCrash) { - SendCrashReport(report) - .recover { case NonFatal(err) => - logSendFailure(reportType = "crash")(err) - } - } + def sendErrorReport(report: telemetry.ErrorReport): Unit = + if (telemetryLevel().enabled) sendErrorReport0(report) - private def logSendFailure(reportType: String)(error: Throwable) = - logger.debug(s"Failed to send $reportType report: ${error}") + def sendCrashReport(report: telemetry.CrashReport): Unit = + if (telemetryLevel().enabled) sendCrashReport0(report) } diff --git a/metals/src/main/scala/scala/meta/internal/telemetry/TelemetryReportContext.scala b/metals/src/main/scala/scala/meta/internal/telemetry/TelemetryReportContext.scala index 05403bb62ec..5e2a87318c7 100644 --- a/metals/src/main/scala/scala/meta/internal/telemetry/TelemetryReportContext.scala +++ b/metals/src/main/scala/scala/meta/internal/telemetry/TelemetryReportContext.scala @@ -3,12 +3,15 @@ package scala.meta.internal.telemetry import java.nio.file.Path import java.{util => ju} +import scala.concurrent.ExecutionContext + import scala.meta.internal.metals.LoggerAccess import scala.meta.internal.metals.ReportSanitizer import scala.meta.internal.metals.SourceCodeSanitizer import scala.meta.internal.metals.SourceCodeTransformer import scala.meta.internal.metals.TelemetryLevel import scala.meta.internal.metals.WorkspaceSanitizer +import scala.meta.internal.mtags.CommonMtagsEnrichments.XtensionOptionalJava import scala.meta.internal.telemetry import scala.meta.pc.Report import scala.meta.pc.ReportContext @@ -48,7 +51,7 @@ class TelemetryReportContext( telemetryClientConfig: TelemetryClient.Config = TelemetryClient.Config.default, logger: LoggerAccess = LoggerAccess.system, -) extends ReportContext { +)(implicit ec: ExecutionContext) extends ReportContext { // Don't send reports with fragile user data - sources etc override lazy val unsanitized: Reporter = reporter("unsanitized") @@ -59,7 +62,7 @@ class TelemetryReportContext( config = telemetryClientConfig, telemetryLevel = telemetryLevel, logger = logger, - ) + )(ec) private def reporter(name: String) = new TelemetryReporter( name = name, @@ -82,44 +85,39 @@ private class TelemetryReporter( override def getReports(): ju.List[TimestampedFile] = ju.Collections.emptyList() - override def cleanUpOldReports( - maxReportsNumber: Int - ): ju.List[TimestampedFile] = + + override def cleanUpOldReports(maxReportsNumber: Int): ju.List[TimestampedFile] = ju.Collections.emptyList() + override def deleteAll(): Unit = () override def sanitize(message: String): String = sanitizers.all.foldRight(message)(_.apply(_)) - private def createSanitizedReport(report: Report) = new telemetry.ErrorReport( - /* name = */ report.name, - /* text = */ if (sanitizers.canSanitizeSources) - ju.Optional.of(sanitize(report.text)) - else ju.Optional.empty(), - /* id = */ report.id, - /* error = */ report.error.map( - telemetry.ExceptionSummary.fromThrowable(_, sanitize(_)) - ), - /* reporterName = */ name, - /* reporterContext = */ reporterContext() match { - case ctx: telemetry.MetalsLspContext => - telemetry.ReporterContextUnion.metalsLSP(ctx) - case ctx: telemetry.ScalaPresentationCompilerContext => - telemetry.ReporterContextUnion.scalaPresentationCompiler(ctx) - }, - ) + private def createSanitizedReport(report: Report) = { + new telemetry.ErrorReport( + name = report.name, + reporterName = name, + reporterContext = reporterContext(), + id = report.id.asScala, + text = Option.when(sanitizers.canSanitizeSources)(sanitize(report.text)), + error = report.error + .map(telemetry.ExceptionSummary.from(_, sanitize(_))) + .asScala, + ) + } override def create( unsanitizedReport: Report, ifVerbose: Boolean, ): ju.Optional[Path] = { - if (telemetryLevel().reportErrors) { + if (telemetryLevel() == TelemetryLevel.Full) { val report = createSanitizedReport(unsanitizedReport) - if (report.getText().isPresent() || report.getError().isPresent()) + if (report.text.isDefined || report.error.isDefined) client.sendErrorReport(report) else logger.info( - "Skiped reporting remotely unmeaningful report, no context or error, reportId=" + + "Skipped reporting remotely unmeaningful report, no context or error, reportId=" + unsanitizedReport.id.orElse("null") ) } diff --git a/metals/src/main/scala/scala/meta/internal/telemetry/conversion/package.scala b/metals/src/main/scala/scala/meta/internal/telemetry/conversion/package.scala index 8238d06935d..8f13a7e2408 100644 --- a/metals/src/main/scala/scala/meta/internal/telemetry/conversion/package.scala +++ b/metals/src/main/scala/scala/meta/internal/telemetry/conversion/package.scala @@ -1,7 +1,5 @@ package scala.meta.internal.telemetry -import java.{util => ju} - import scala.collection.JavaConverters._ import scala.jdk.OptionConverters._ @@ -16,67 +14,55 @@ package object conversion { def UserConfiguration( config: metals.UserConfiguration ): MetalsUserConfiguration = - new MetalsUserConfiguration( - /* symbolPrefixes = */ config.symbolPrefixes.asJava, - /* bloopSbtAlreadyInstalled = */ config.bloopSbtAlreadyInstalled, - /* bloopVersion = */ config.bloopVersion.toJava, - /* bloopJvmProperties = */ config.bloopJvmProperties.toList.flatten.asJava, - /* ammoniteJvmProperties = */ config.ammoniteJvmProperties.toList.flatten.asJava, - /* superMethodLensesEnabled = */ config.superMethodLensesEnabled, - /* showInferredType = */ config.showInferredType.toJava, - /* showImplicitArguments = */ config.showImplicitArguments, - /* showImplicitConversionsAndClasses = */ config.showImplicitConversionsAndClasses, - /* enableStripMarginOnTypeFormatting = */ config.enableStripMarginOnTypeFormatting, - /* enableIndentOnPaste = */ config.enableIndentOnPaste, - /* enableSemanticHighlighting = */ config.enableSemanticHighlighting, - /* excludedPackages = */ config.excludedPackages.toList.flatten.asJava, - /* fallbackScalaVersion = */ config.fallbackScalaVersion.toJava, - /* testUserInterface = */ TestUserInterfaceKind(config.testUserInterface), + MetalsUserConfiguration( + symbolPrefixes = config.symbolPrefixes, + bloopSbtAlreadyInstalled = config.bloopSbtAlreadyInstalled, + bloopVersion = config.bloopVersion, + bloopJvmProperties = config.bloopJvmProperties.toList.flatten, + ammoniteJvmProperties = config.ammoniteJvmProperties.toList.flatten, + superMethodLensesEnabled = config.superMethodLensesEnabled, + showInferredType = config.showInferredType, + showImplicitArguments = config.showImplicitArguments, + showImplicitConversionsAndClasses = + config.showImplicitConversionsAndClasses, + enableStripMarginOnTypeFormatting = + config.enableStripMarginOnTypeFormatting, + enableIndentOnPaste = config.enableIndentOnPaste, + enableSemanticHighlighting = config.enableSemanticHighlighting, + excludedPackages = config.excludedPackages.toList.flatten, + fallbackScalaVersion = config.fallbackScalaVersion, + testUserInterface = TestUserInterfaceKind(config.testUserInterface), ) def PresentationCompilerConfig( config: pc.PresentationCompilerConfig ): telemetry.PresentationCompilerConfig = - new telemetry.PresentationCompilerConfig( - copyOf(config.symbolPrefixes), - config.completionCommand, - config.parameterHintsCommand(), - config.overrideDefFormat.name(), - config.isDefaultSymbolPrefixes(), - config.isCompletionItemDetailEnabled(), - config.isStripMarginOnTypeFormattingEnabled(), - config.isCompletionItemDocumentationEnabled(), - config.isHoverDocumentationEnabled(), - config.snippetAutoIndent(), - config.isSignatureHelpDocumentationEnabled(), - config.isCompletionSnippetsEnabled(), - copyOf(config.semanticdbCompilerOptions), + telemetry.PresentationCompilerConfig( + symbolPrefixes = config.symbolPrefixes().asScala.toMap, + completionCommand = config.completionCommand().asScala, + parameterHintsCommand = config.parameterHintsCommand().asScala, + overrideDefFormat = config.overrideDefFormat.name(), + isDefaultSymbolPrefixes = config.isDefaultSymbolPrefixes(), + isCompletionItemDetailEnabled = config.isCompletionItemDetailEnabled(), + isStripMarginOnTypeFormattingEnabled = + config.isStripMarginOnTypeFormattingEnabled(), + isCompletionItemDocumentationEnabled = + config.isCompletionItemDocumentationEnabled(), + isHoverDocumentationEnabled = config.isHoverDocumentationEnabled(), + snippetAutoIndent = config.snippetAutoIndent(), + isSignatureHelpDocumentationEnabled = + config.isSignatureHelpDocumentationEnabled(), + isCompletionSnippetsEnabled = config.isCompletionSnippetsEnabled(), + semanticdbCompilerOptions = + config.semanticdbCompilerOptions.asScala.toList, ) - // Java Collections utilities not available in JDK 8 - private def copyOf[T](v: ju.List[T]): ju.List[T] = { - val copy = new ju.ArrayList[T](v.size()) - copy.addAll(v) - copy - } - - private def copyOf[K, V](v: ju.Map[K, V]): ju.Map[K, V] = { - val copy = new ju.HashMap[K, V](v.size()) - copy.putAll(v) - copy - } - def BuildServerConnections( session: bsp.BspSession ): List[BuildServerConnection] = { - def convert( - conn: metals.BuildServerConnection, - isMain: Boolean, - ) = new BuildServerConnection( - /* name = */ conn.name, - /* version = */ conn.version, - /* isMain = */ isMain, - ) + def convert(conn: metals.BuildServerConnection, isMain: Boolean) = + BuildServerConnection(conn.name, conn.version, isMain) + convert(session.main, isMain = true) :: session.meta.map(convert(_, isMain = false)) } @@ -90,20 +76,20 @@ package object conversion { def MetalsServerConfig( config: metals.MetalsServerConfig ): MetalsServerConfiguration = - new MetalsServerConfiguration( - /* executeClientCommand = */ config.executeClientCommand.value, - /* snippetAutoIndent = */ config.snippetAutoIndent, - /* isHttpEnabled = */ config.isHttpEnabled, - /* isInputBoxEnabled = */ config.isInputBoxEnabled, - /* askToReconnect = */ config.askToReconnect, - /* allowMultilineStringFormatting = */ config.allowMultilineStringFormatting, - /* compilers = */ PresentationCompilerConfig(config.compilers), + MetalsServerConfiguration( + config.executeClientCommand.value, + config.snippetAutoIndent, + config.isHttpEnabled, + config.isInputBoxEnabled, + config.askToReconnect, + config.allowMultilineStringFormatting, + PresentationCompilerConfig(config.compilers), ) def MetalsClientInfo(info: lsp4j.ClientInfo): telemetry.MetalsClientInfo = - new telemetry.MetalsClientInfo( - /* name = */ ju.Optional.of(info.getName()), - /* version = */ ju.Optional.of(info.getVersion()), + telemetry.MetalsClientInfo( + Option(info.getName()), + Option(info.getVersion()), ) } diff --git a/metals/src/main/scala/scala/meta/metals/Main.scala b/metals/src/main/scala/scala/meta/metals/Main.scala index fcaf60544bb..3b5e1e422cc 100644 --- a/metals/src/main/scala/scala/meta/metals/Main.scala +++ b/metals/src/main/scala/scala/meta/metals/Main.scala @@ -1,6 +1,5 @@ package scala.meta.metals -import java.util.Optional import java.util.concurrent.Executors import scala.concurrent.ExecutionContext @@ -67,7 +66,7 @@ object Main { launcher.startListening().get() } catch { case NonFatal(e) => - trySendCrashReport(e, server) + trySendCrashReport(e, server, ec) e.printStackTrace(systemOut) sys.exit(1) } finally { @@ -82,16 +81,16 @@ object Main { private def trySendCrashReport( error: Throwable, server: MetalsLanguageServer, + ec: ExecutionContext, ): Unit = try { val telemetryLevel = server.getTelemetryLevel() - if (telemetryLevel.reportCrash) { - val telemetry = new TelemetryClient(() => telemetryLevel) + if (telemetryLevel.enabled) { + val telemetry = new TelemetryClient(() => telemetryLevel)(ec) telemetry.sendCrashReport( - new CrashReport( - ExceptionSummary.fromThrowable(error, identity), - this.getClass().getName(), - Optional.of(BuildInfo.metalsVersion), - Optional.empty(), + CrashReport( + error = ExceptionSummary.from(error, identity), + componentName = this.getClass().getName(), + componentVersion = Some(BuildInfo.metalsVersion), ) ) } diff --git a/metals/src/main/scala/scala/meta/metals/MetalsLanguageServer.scala b/metals/src/main/scala/scala/meta/metals/MetalsLanguageServer.scala index 2eba172738a..44c22bb0439 100644 --- a/metals/src/main/scala/scala/meta/metals/MetalsLanguageServer.scala +++ b/metals/src/main/scala/scala/meta/metals/MetalsLanguageServer.scala @@ -284,19 +284,24 @@ class MetalsLanguageServer( case _ => throw new IllegalStateException("Server is not initialized") } - private[metals] def getTelemetryLevel() = { - def maxConfiguredTelemetryLevel(service: WorkspaceLspService) = { - val entries = - service.workspaceFolders.getFolderServices.map(_.getTelemetryLevel) - if (entries.isEmpty) TelemetryLevel.discover - else entries.max + /** + * Telemetry level used only for initialisation purpose. + * It will return the minimal level set, within all workspaces e.g. + * - WorkspaceA - telemetry off, + * - WorkspaceB - telemetry full, + * the method will return telemetry off + */ + private[metals] def getTelemetryLevel(): TelemetryLevel = { + serverState.get() + def lowestSettingWithinWorkspaces(service: WorkspaceLspService) = { + service.workspaceFolders.getFolderServices.map(_.getTelemetryLevel).min } serverState.get() match { case ServerState.Initialized(service) => - maxConfiguredTelemetryLevel(service) + lowestSettingWithinWorkspaces(service) case ServerState.ShuttingDown(service) => - maxConfiguredTelemetryLevel(service) - case _ => TelemetryLevel.discover + lowestSettingWithinWorkspaces(service) + case _ => TelemetryLevel.Off } } diff --git a/metals/src/main/scala/scala/meta/metals/ServerState.scala b/metals/src/main/scala/scala/meta/metals/ServerState.scala index 8f2605ff226..85a73b11d7c 100644 --- a/metals/src/main/scala/scala/meta/metals/ServerState.scala +++ b/metals/src/main/scala/scala/meta/metals/ServerState.scala @@ -12,6 +12,5 @@ sealed trait ServerState object ServerState { case object Started extends ServerState final case class Initialized(service: WorkspaceLspService) extends ServerState - final case class ShuttingDown(service: WorkspaceLspService) - extends ServerState + final case class ShuttingDown(service: WorkspaceLspService) extends ServerState } diff --git a/mtags-shared/src/main/scala/scala/meta/internal/metals/SourceCodeSanitizer.scala b/mtags-shared/src/main/scala/scala/meta/internal/metals/SourceCodeSanitizer.scala index 7c3a2a906f5..d5524456a8d 100644 --- a/mtags-shared/src/main/scala/scala/meta/internal/metals/SourceCodeSanitizer.scala +++ b/mtags-shared/src/main/scala/scala/meta/internal/metals/SourceCodeSanitizer.scala @@ -39,12 +39,12 @@ class SourceCodeSanitizer[ParserCtx, ParserAST]( .map(_.trim()) .filter(_.nonEmpty) .map(_.replaceAll(OffsetMarker, "")) - .fold[Either[String, String]](Left("no-source")) { source => + .fold[Either[String, String]](Left("")) { source => if (StackTraceLine.findFirstIn(source).isDefined) Right(source) else if (languageHint.forall(_ == Language.Scala)) { val maybeParseResult = parser.parse(source) - if (maybeParseResult.isEmpty) Left("") else { val (ctx, tree) = maybeParseResult.get val maybeSanitizedTree = parser.transformer.sanitizeSymbols(tree) diff --git a/mtags-shared/src/main/scala/scala/meta/internal/metals/SourceCodeTransformer.scala b/mtags-shared/src/main/scala/scala/meta/internal/metals/SourceCodeTransformer.scala index 6da8123b8bb..55454679135 100644 --- a/mtags-shared/src/main/scala/scala/meta/internal/metals/SourceCodeTransformer.scala +++ b/mtags-shared/src/main/scala/scala/meta/internal/metals/SourceCodeTransformer.scala @@ -134,10 +134,7 @@ trait SourceCodeTransformer[Context, Tree] { originalSymbol, { if (originalSymbol.length() <= ShortSymbolLength) originalSymbol else generateSymbol(originalSymbol, suffix) - }.ensuring( - _.length() == originalSymbol.length(), - "new symbol has different length then original" - ) + } ) private def generateSymbol( diff --git a/mtags-shared/src/main/scala/scala/meta/internal/metals/TelemetryLevel.scala b/mtags-shared/src/main/scala/scala/meta/internal/metals/TelemetryLevel.scala index 42307428ab1..2b94e3723b4 100644 --- a/mtags-shared/src/main/scala/scala/meta/internal/metals/TelemetryLevel.scala +++ b/mtags-shared/src/main/scala/scala/meta/internal/metals/TelemetryLevel.scala @@ -1,35 +1,41 @@ package scala.meta.internal.metals -sealed class TelemetryLevel( - private[TelemetryLevel] val level: Int, - val stringValue: String -) { - def enabled: Boolean = level > TelemetryLevel.Off.level - def reportCrash: Boolean = level >= TelemetryLevel.Crash.level - def reportErrors: Boolean = level >= TelemetryLevel.Error.level - def reportAll: Boolean = level >= TelemetryLevel.All.level +trait TelemetryLevel { + val textValue: String + val enabled: Boolean + protected val level: Int } object TelemetryLevel { - implicit lazy val ordering: Ordering[TelemetryLevel] = Ordering.by(_.level) - - case object Off extends TelemetryLevel(0, "off") - case object Crash extends TelemetryLevel(1, "crash") - case object Error extends TelemetryLevel(2, "error") - case object All extends TelemetryLevel(Int.MaxValue, "all") - - def default: TelemetryLevel = Off - def discover: TelemetryLevel = sys.props - .get(SystemPropertyKey) - .flatMap(fromString) - .getOrElse(default) - - final val SystemPropertyKey = "metals.telemetry-level" - - def fromString(value: String): Option[TelemetryLevel] = - Option(value).map(_.trim().toLowerCase()).collect { - case Off.stringValue => Off - case Error.stringValue => Error - case All.stringValue => All + + implicit val levelOrdering: Ordering[TelemetryLevel] = Ordering.by(_.level) + + case object Off extends TelemetryLevel { + val textValue: String = "off" + val level: Int = 0 + val enabled = false + } + + case object Anonymous extends TelemetryLevel { + val textValue: String = "anonymous" + val level: Int = 1 + val enabled = true + } + + case object Full extends TelemetryLevel { + val textValue: String = "full" + val level: Int = 2 + val enabled = true + } + + val default = Off + + def fromString(textValue: String): TelemetryLevel = { + textValue match { + case Anonymous.textValue => Anonymous + case Full.textValue => Full + case _ => Off } + } } + diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/BuildServerConnection.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/BuildServerConnection.java deleted file mode 100644 index e5b8dd5ab71..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/BuildServerConnection.java +++ /dev/null @@ -1,60 +0,0 @@ -package scala.meta.internal.telemetry; - -public class BuildServerConnection { - final private String name; - final private String version; - final private boolean isMain; - - public BuildServerConnection(String name, String version, boolean isMain) { - this.name = name; - this.version = version; - this.isMain = isMain; - } - - public String getName() { - return name; - } - - public String getVersion() { - return version; - } - - public boolean isMain() { - return isMain; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - result = prime * result + (isMain ? 1231 : 1237); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BuildServerConnection other = (BuildServerConnection) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (version == null) { - if (other.version != null) - return false; - } else if (!version.equals(other.version)) - return false; - if (isMain != other.isMain) - return false; - return true; - } - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/CrashReport.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/CrashReport.java deleted file mode 100644 index a3eaffb4359..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/CrashReport.java +++ /dev/null @@ -1,46 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.Optional; - -public class CrashReport { - final private ExceptionSummary error; - final private String componentName; - final private Environment env; - final private Optional componentVersion; - final private Optional reporterContext; - - public CrashReport(ExceptionSummary error, String componentName, Optional componentVersion, - Optional reporterContext) { - this(error, componentName, Environment.get(), componentVersion, reporterContext); - } - - public CrashReport(ExceptionSummary error, String componentName, Environment env, Optional componentVersion, - Optional reporterContext) { - this.error = error; - this.componentName = componentName; - this.env = env; - this.componentVersion = componentVersion; - this.reporterContext = reporterContext; - } - - public ExceptionSummary getError() { - return error; - } - - public String getComponentName() { - return componentName; - } - - public Optional getComponentVersion() { - return componentVersion; - } - - public Optional getReporterContext() { - return reporterContext; - } - - public Environment getEnv() { - return env; - } - -} diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/Environment.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/Environment.java deleted file mode 100644 index 9713a8980f5..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/Environment.java +++ /dev/null @@ -1,66 +0,0 @@ -package scala.meta.internal.telemetry; - -public class Environment { - final private JavaInfo java; - final private SystemInfo system; - - private final static Environment instance; - - public static Environment get() { - return instance; - } - - static { - instance = new Environment( - new JavaInfo(System.getProperty("java.version", "unknown"), - System.getProperty("java.vendor", "unknown")), - new SystemInfo(System.getProperty("os.arch", "unknown"), System.getProperty("os.name", "unknown"), - System.getProperty("os.version", "unknown"))); - } - - // Generated - - public Environment(JavaInfo java, SystemInfo system) { - this.java = java; - this.system = system; - } - - public JavaInfo getJava() { - return java; - } - - public SystemInfo getSystem() { - return system; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((java == null) ? 0 : java.hashCode()); - result = prime * result + ((system == null) ? 0 : system.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Environment other = (Environment) obj; - if (java == null) { - if (other.java != null) - return false; - } else if (!java.equals(other.java)) - return false; - if (system == null) { - if (other.system != null) - return false; - } else if (!system.equals(other.system)) - return false; - return true; - } -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ErrorReport.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ErrorReport.java deleted file mode 100644 index 2a1295931dc..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ErrorReport.java +++ /dev/null @@ -1,119 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.Optional; - -public class ErrorReport { - final private String name; - final private Optional text; - final private Optional id; - final private Optional error; - final private String reporterName; - final private ReporterContextUnion reporterContext; - final private Environment env; - - public ErrorReport(String name, Optional text, Optional id, Optional error, - String reporterName, ReporterContextUnion reporterContext) { - this(name, text, id, error, reporterName, reporterContext, Environment.get()); - } - - public ErrorReport(String name, Optional text, Optional id, Optional error, - String reporterName, ReporterContextUnion reporterContext, Environment env) { - this.name = name; - this.text = text; - this.id = id; - this.error = error; - this.reporterName = reporterName; - this.reporterContext = reporterContext; - this.env = env; - } - - public String getName() { - return name; - } - - public Optional getText() { - return text; - } - - public Optional getId() { - return id; - } - - public Optional getError() { - return error; - } - - public String getReporterName() { - return reporterName; - } - - public ReporterContextUnion getReporterContext() { - return reporterContext; - } - - public Environment getEnv() { - return env; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((text == null) ? 0 : text.hashCode()); - result = prime * result + ((id == null) ? 0 : id.hashCode()); - result = prime * result + ((error == null) ? 0 : error.hashCode()); - result = prime * result + ((reporterName == null) ? 0 : reporterName.hashCode()); - result = prime * result + ((reporterContext == null) ? 0 : reporterContext.hashCode()); - result = prime * result + ((env == null) ? 0 : env.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ErrorReport other = (ErrorReport) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (text == null) { - if (other.text != null) - return false; - } else if (!text.equals(other.text)) - return false; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - if (error == null) { - if (other.error != null) - return false; - } else if (!error.equals(other.error)) - return false; - if (reporterName == null) { - if (other.reporterName != null) - return false; - } else if (!reporterName.equals(other.reporterName)) - return false; - if (reporterContext == null) { - if (other.reporterContext != null) - return false; - } else if (!reporterContext.equals(other.reporterContext)) - return false; - if (env == null) { - if (other.env != null) - return false; - } else if (!env.equals(other.env)) - return false; - return true; - } - -} diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ExceptionSummary.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ExceptionSummary.java deleted file mode 100644 index 9d17efc4b01..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ExceptionSummary.java +++ /dev/null @@ -1,70 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.List; -import java.util.function.Function; - -public class ExceptionSummary { - final private List exceptions; - final private String stacktrace; - - public ExceptionSummary(List exceptions, String stacktrace) { - this.exceptions = exceptions; - this.stacktrace = stacktrace; - } - - public List getExceptions() { - return exceptions; - } - - public String getStacktrace() { - return stacktrace; - } - - - public static ExceptionSummary fromThrowable(Throwable exception, Function sanitizer) { - List exceptions = new java.util.LinkedList<>(); - for (Throwable current = exception; current != null; current = current.getCause()) { - exceptions.add(current.getClass().getName()); - } - StringWriter stringWriter = new StringWriter(); - try (PrintWriter pw = new PrintWriter(stringWriter)) { - exception.printStackTrace(pw); - } - String stacktrace = sanitizer.apply(stringWriter.toString()); - return new ExceptionSummary(exceptions, stacktrace); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((exceptions == null) ? 0 : exceptions.hashCode()); - result = prime * result + ((stacktrace == null) ? 0 : stacktrace.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ExceptionSummary other = (ExceptionSummary) obj; - if (exceptions == null) { - if (other.exceptions != null) - return false; - } else if (!exceptions.equals(other.exceptions)) - return false; - if (stacktrace == null) { - if (other.stacktrace != null) - return false; - } else if (!stacktrace.equals(other.stacktrace)) - return false; - return true; - } - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/GsonCodecs.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/GsonCodecs.java deleted file mode 100644 index 71ef3edff06..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/GsonCodecs.java +++ /dev/null @@ -1,128 +0,0 @@ -package scala.meta.internal.telemetry; - -import com.google.gson.*; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Optional; -import java.util.function.Consumer; -import java.lang.Iterable; - -public final class GsonCodecs { - - public static class OptionalTypeAdapter extends TypeAdapter> { - - public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - Class rawType = (Class) type.getRawType(); - if (rawType != Optional.class) { - return null; - } - final ParameterizedType parameterizedType = (ParameterizedType) type.getType(); - final Type actualType = parameterizedType.getActualTypeArguments()[0]; - final TypeAdapter adapter = gson.getAdapter(TypeToken.get(actualType)); - return new OptionalTypeAdapter(adapter); - } - }; - private final TypeAdapter adapter; - - public OptionalTypeAdapter(TypeAdapter adapter) { - this.adapter = adapter; - } - - @Override - public Optional read(JsonReader in) throws IOException { - if (in.peek() != JsonToken.NULL) { - return Optional.ofNullable(adapter.read(in)); - } else { - in.nextNull(); - return Optional.empty(); - } - } - - @Override - public void write(JsonWriter out, Optional value) throws IOException { - if (value.isPresent()) { - adapter.write(out, value.get()); - } else { - out.nullValue(); - } - } - } - - final static class PostProcessingTypeAdapterFactory implements TypeAdapterFactory { - static PostProcessingTypeAdapterFactory of(final Consumer... postProcessorsArgs) { - return new PostProcessingTypeAdapterFactory(Arrays.asList(postProcessorsArgs.clone())); - } - - static PostProcessingTypeAdapterFactory of(final Iterable> postProcessorsArgs) { - return new PostProcessingTypeAdapterFactory(postProcessorsArgs); - } - - private final Iterable> postProcessors; - - private PostProcessingTypeAdapterFactory(final Iterable> postProcessors) { - this.postProcessors = postProcessors; - } - - @Override - public TypeAdapter create(final Gson gson, final TypeToken typeToken) { - final TypeAdapter delegateAdapter = gson.getDelegateAdapter(this, typeToken); - return new TypeAdapter() { - @Override - public void write(final JsonWriter out, final T value) throws IOException { - delegateAdapter.write(out, value); - } - - @Override - public T read(final JsonReader in) throws IOException { - final T value = delegateAdapter.read(in); - for (final Consumer postProcessor : postProcessors) { - postProcessor.accept(value); - } - return value; - } - }; - } - } - - /* Sets missing Optional fields in JSON to Optional.empty */ - final static Consumer missingOptionalFieldsPostProcessor = new Consumer() { - @Override - public void accept(final Object o) { - try { - final String objectPackage = o.getClass().getPackage().getName(); - final String telemetryModelPackage = this.getClass().getPackage().getName(); - // Modify only known classess defined in the same package - if (objectPackage.startsWith(telemetryModelPackage)) { - final Field[] declaredFields = o.getClass().getDeclaredFields(); - for (final Field field : declaredFields) { - if (field.getType() == Optional.class && !Modifier.isStatic(field.getModifiers())) { - field.setAccessible(true); - if (field.get(o) == null) { - field.set(o, Optional.empty()); - } - } - } - } - } catch (final IllegalAccessException ex) { - throw new RuntimeException("Failed to fill optional fields in " + o.getClass().getName(), ex); - } - } - }; - - public static Gson gson = new GsonBuilder().disableHtmlEscaping() - .registerTypeAdapterFactory(OptionalTypeAdapter.FACTORY) - .registerTypeAdapterFactory(PostProcessingTypeAdapterFactory.of(missingOptionalFieldsPostProcessor)) - .create(); - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/JavaInfo.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/JavaInfo.java deleted file mode 100644 index 8f677456ae1..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/JavaInfo.java +++ /dev/null @@ -1,53 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.Optional; - -public class JavaInfo { - final private String version; - final private String distribution; - - public JavaInfo(String version, String distribution) { - this.version = version; - this.distribution = distribution; - } - - public String getVersion() { - return version; - } - - public String getDistribution() { - return distribution; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((version == null) ? 0 : version.hashCode()); - result = prime * result + ((distribution == null) ? 0 : distribution.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - JavaInfo other = (JavaInfo) obj; - if (version == null) { - if (other.version != null) - return false; - } else if (!version.equals(other.version)) - return false; - if (distribution == null) { - if (other.distribution != null) - return false; - } else if (!distribution.equals(other.distribution)) - return false; - return true; - } - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsClientInfo.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsClientInfo.java deleted file mode 100644 index df6f5cd6e1d..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsClientInfo.java +++ /dev/null @@ -1,53 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.Optional; - -public class MetalsClientInfo { - final private Optional name; - final private Optional version; - - public MetalsClientInfo(Optional name, Optional version) { - this.name = name; - this.version = version; - } - - public Optional getName() { - return name; - } - - public Optional getVersion() { - return version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - MetalsClientInfo other = (MetalsClientInfo) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (version == null) { - if (other.version != null) - return false; - } else if (!version.equals(other.version)) - return false; - return true; - } - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsLspContext.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsLspContext.java deleted file mode 100644 index 7b3f6e4ebe2..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsLspContext.java +++ /dev/null @@ -1,91 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.List; - -public class MetalsLspContext implements ReporterContext { - final private String metalsVersion; - final private MetalsUserConfiguration userConfig; - final private MetalsServerConfiguration serverConfig; - final private MetalsClientInfo clientInfo; - final private List buildServerConnections; - - public MetalsLspContext(String metalsVersion, MetalsUserConfiguration userConfig, - MetalsServerConfiguration serverConfig, MetalsClientInfo clientInfo, - List buildServerConnections) { - this.metalsVersion = metalsVersion; - this.userConfig = userConfig; - this.serverConfig = serverConfig; - this.clientInfo = clientInfo; - this.buildServerConnections = buildServerConnections; - } - - public String getMetalsVersion() { - return metalsVersion; - } - - public MetalsUserConfiguration getUserConfig() { - return userConfig; - } - - public MetalsServerConfiguration getServerConfig() { - return serverConfig; - } - - public MetalsClientInfo getClientInfo() { - return clientInfo; - } - - public List getBuildServerConnections() { - return buildServerConnections; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((metalsVersion == null) ? 0 : metalsVersion.hashCode()); - result = prime * result + ((userConfig == null) ? 0 : userConfig.hashCode()); - result = prime * result + ((serverConfig == null) ? 0 : serverConfig.hashCode()); - result = prime * result + ((clientInfo == null) ? 0 : clientInfo.hashCode()); - result = prime * result + ((buildServerConnections == null) ? 0 : buildServerConnections.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - MetalsLspContext other = (MetalsLspContext) obj; - if (metalsVersion == null) { - if (other.metalsVersion != null) - return false; - } else if (!metalsVersion.equals(other.metalsVersion)) - return false; - if (userConfig == null) { - if (other.userConfig != null) - return false; - } else if (!userConfig.equals(other.userConfig)) - return false; - if (serverConfig == null) { - if (other.serverConfig != null) - return false; - } else if (!serverConfig.equals(other.serverConfig)) - return false; - if (clientInfo == null) { - if (other.clientInfo != null) - return false; - } else if (!clientInfo.equals(other.clientInfo)) - return false; - if (buildServerConnections == null) { - if (other.buildServerConnections != null) - return false; - } else if (!buildServerConnections.equals(other.buildServerConnections)) - return false; - return true; - } - -} diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsServerConfiguration.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsServerConfiguration.java deleted file mode 100644 index c92dee1ce5f..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsServerConfiguration.java +++ /dev/null @@ -1,98 +0,0 @@ -package scala.meta.internal.telemetry; - -public class MetalsServerConfiguration { - final private String executeClientCommand; - final private boolean snippetAutoIndent; - final private boolean isHttpEnabled; - final private boolean isInputBoxEnabled; - final private boolean askToReconnect; - final private boolean allowMultilineStringFormatting; - final private PresentationCompilerConfig compilers; - - public MetalsServerConfiguration(String executeClientCommand, boolean snippetAutoIndent, boolean isHttpEnabled, - boolean isInputBoxEnabled, boolean askToReconnect, boolean allowMultilineStringFormatting, - PresentationCompilerConfig compilers) { - this.executeClientCommand = executeClientCommand; - this.snippetAutoIndent = snippetAutoIndent; - this.isHttpEnabled = isHttpEnabled; - this.isInputBoxEnabled = isInputBoxEnabled; - this.askToReconnect = askToReconnect; - this.allowMultilineStringFormatting = allowMultilineStringFormatting; - this.compilers = compilers; - } - - public String getExecuteClientCommand() { - return executeClientCommand; - } - - public boolean isSnippetAutoIndent() { - return snippetAutoIndent; - } - - public boolean isHttpEnabled() { - return isHttpEnabled; - } - - public boolean isInputBoxEnabled() { - return isInputBoxEnabled; - } - - public boolean isAskToReconnect() { - return askToReconnect; - } - - public boolean isAllowMultilineStringFormatting() { - return allowMultilineStringFormatting; - } - - public PresentationCompilerConfig getCompilers() { - return compilers; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((executeClientCommand == null) ? 0 : executeClientCommand.hashCode()); - result = prime * result + (snippetAutoIndent ? 1231 : 1237); - result = prime * result + (isHttpEnabled ? 1231 : 1237); - result = prime * result + (isInputBoxEnabled ? 1231 : 1237); - result = prime * result + (askToReconnect ? 1231 : 1237); - result = prime * result + (allowMultilineStringFormatting ? 1231 : 1237); - result = prime * result + ((compilers == null) ? 0 : compilers.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - MetalsServerConfiguration other = (MetalsServerConfiguration) obj; - if (executeClientCommand == null) { - if (other.executeClientCommand != null) - return false; - } else if (!executeClientCommand.equals(other.executeClientCommand)) - return false; - if (snippetAutoIndent != other.snippetAutoIndent) - return false; - if (isHttpEnabled != other.isHttpEnabled) - return false; - if (isInputBoxEnabled != other.isInputBoxEnabled) - return false; - if (askToReconnect != other.askToReconnect) - return false; - if (allowMultilineStringFormatting != other.allowMultilineStringFormatting) - return false; - if (compilers == null) { - if (other.compilers != null) - return false; - } else if (!compilers.equals(other.compilers)) - return false; - return true; - } - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsUserConfiguration.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsUserConfiguration.java deleted file mode 100644 index 6542cfd240d..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/MetalsUserConfiguration.java +++ /dev/null @@ -1,194 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class MetalsUserConfiguration { - final private Map symbolPrefixes; - final private boolean bloopSbtAlreadyInstalled; - final private Optional bloopVersion; - final private List bloopJvmProperties; - final private List ammoniteJvmProperties; - final private boolean superMethodLensesEnabled; - final private Optional showInferredType; - final private boolean showImplicitArguments; - final private boolean showImplicitConversionsAndClasses; - final private boolean enableStripMarginOnTypeFormatting; - final private boolean enableIndentOnPaste; - final private boolean enableSemanticHighlighting; - final private List excludedPackages; - final private Optional fallbackScalaVersion; - final private String testUserInterface; - - public MetalsUserConfiguration(Map symbolPrefixes, boolean bloopSbtAlreadyInstalled, - Optional bloopVersion, List bloopJvmProperties, List ammoniteJvmProperties, - boolean superMethodLensesEnabled, Optional showInferredType, boolean showImplicitArguments, - boolean showImplicitConversionsAndClasses, boolean enableStripMarginOnTypeFormatting, - boolean enableIndentOnPaste, boolean enableSemanticHighlighting, List excludedPackages, - Optional fallbackScalaVersion, String testUserInterface) { - this.symbolPrefixes = symbolPrefixes; - this.bloopSbtAlreadyInstalled = bloopSbtAlreadyInstalled; - this.bloopVersion = bloopVersion; - this.bloopJvmProperties = bloopJvmProperties; - this.ammoniteJvmProperties = ammoniteJvmProperties; - this.superMethodLensesEnabled = superMethodLensesEnabled; - this.showInferredType = showInferredType; - this.showImplicitArguments = showImplicitArguments; - this.showImplicitConversionsAndClasses = showImplicitConversionsAndClasses; - this.enableStripMarginOnTypeFormatting = enableStripMarginOnTypeFormatting; - this.enableIndentOnPaste = enableIndentOnPaste; - this.enableSemanticHighlighting = enableSemanticHighlighting; - this.excludedPackages = excludedPackages; - this.fallbackScalaVersion = fallbackScalaVersion; - this.testUserInterface = testUserInterface; - } - - public Map getSymbolPrefixes() { - return symbolPrefixes; - } - - public boolean isBloopSbtAlreadyInstalled() { - return bloopSbtAlreadyInstalled; - } - - public Optional getBloopVersion() { - return bloopVersion; - } - - public List getBloopJvmProperties() { - return bloopJvmProperties; - } - - public List getAmmoniteJvmProperties() { - return ammoniteJvmProperties; - } - - public boolean isSuperMethodLensesEnabled() { - return superMethodLensesEnabled; - } - - public Optional getShowInferredType() { - return showInferredType; - } - - public boolean isShowImplicitArguments() { - return showImplicitArguments; - } - - public boolean isShowImplicitConversionsAndClasses() { - return showImplicitConversionsAndClasses; - } - - public boolean isEnableStripMarginOnTypeFormatting() { - return enableStripMarginOnTypeFormatting; - } - - public boolean isEnableIndentOnPaste() { - return enableIndentOnPaste; - } - - public boolean isEnableSemanticHighlighting() { - return enableSemanticHighlighting; - } - - public List getExcludedPackages() { - return excludedPackages; - } - - public Optional getFallbackScalaVersion() { - return fallbackScalaVersion; - } - - public String getTestUserInterface() { - return testUserInterface; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((symbolPrefixes == null) ? 0 : symbolPrefixes.hashCode()); - result = prime * result + (bloopSbtAlreadyInstalled ? 1231 : 1237); - result = prime * result + ((bloopVersion == null) ? 0 : bloopVersion.hashCode()); - result = prime * result + ((bloopJvmProperties == null) ? 0 : bloopJvmProperties.hashCode()); - result = prime * result + ((ammoniteJvmProperties == null) ? 0 : ammoniteJvmProperties.hashCode()); - result = prime * result + (superMethodLensesEnabled ? 1231 : 1237); - result = prime * result + ((showInferredType == null) ? 0 : showInferredType.hashCode()); - result = prime * result + (showImplicitArguments ? 1231 : 1237); - result = prime * result + (showImplicitConversionsAndClasses ? 1231 : 1237); - result = prime * result + (enableStripMarginOnTypeFormatting ? 1231 : 1237); - result = prime * result + (enableIndentOnPaste ? 1231 : 1237); - result = prime * result + (enableSemanticHighlighting ? 1231 : 1237); - result = prime * result + ((excludedPackages == null) ? 0 : excludedPackages.hashCode()); - result = prime * result + ((fallbackScalaVersion == null) ? 0 : fallbackScalaVersion.hashCode()); - result = prime * result + ((testUserInterface == null) ? 0 : testUserInterface.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - MetalsUserConfiguration other = (MetalsUserConfiguration) obj; - if (symbolPrefixes == null) { - if (other.symbolPrefixes != null) - return false; - } else if (!symbolPrefixes.equals(other.symbolPrefixes)) - return false; - if (bloopSbtAlreadyInstalled != other.bloopSbtAlreadyInstalled) - return false; - if (bloopVersion == null) { - if (other.bloopVersion != null) - return false; - } else if (!bloopVersion.equals(other.bloopVersion)) - return false; - if (bloopJvmProperties == null) { - if (other.bloopJvmProperties != null) - return false; - } else if (!bloopJvmProperties.equals(other.bloopJvmProperties)) - return false; - if (ammoniteJvmProperties == null) { - if (other.ammoniteJvmProperties != null) - return false; - } else if (!ammoniteJvmProperties.equals(other.ammoniteJvmProperties)) - return false; - if (superMethodLensesEnabled != other.superMethodLensesEnabled) - return false; - if (showInferredType == null) { - if (other.showInferredType != null) - return false; - } else if (!showInferredType.equals(other.showInferredType)) - return false; - if (showImplicitArguments != other.showImplicitArguments) - return false; - if (showImplicitConversionsAndClasses != other.showImplicitConversionsAndClasses) - return false; - if (enableStripMarginOnTypeFormatting != other.enableStripMarginOnTypeFormatting) - return false; - if (enableIndentOnPaste != other.enableIndentOnPaste) - return false; - if (enableSemanticHighlighting != other.enableSemanticHighlighting) - return false; - if (excludedPackages == null) { - if (other.excludedPackages != null) - return false; - } else if (!excludedPackages.equals(other.excludedPackages)) - return false; - if (fallbackScalaVersion == null) { - if (other.fallbackScalaVersion != null) - return false; - } else if (!fallbackScalaVersion.equals(other.fallbackScalaVersion)) - return false; - if (testUserInterface == null) { - if (other.testUserInterface != null) - return false; - } else if (!testUserInterface.equals(other.testUserInterface)) - return false; - return true; - } -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/PresentationCompilerConfig.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/PresentationCompilerConfig.java deleted file mode 100644 index 63908072760..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/PresentationCompilerConfig.java +++ /dev/null @@ -1,168 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class PresentationCompilerConfig { - final private Map symbolPrefixes; - final private Optional completionCommand; - final private Optional parameterHintsCommand; - final private String overrideDefFormat; - final private boolean isDefaultSymbolPrefixes; - final private boolean isCompletionItemDetailEnabled; - final private boolean isStripMarginOnTypeFormattingEnabled; - final private boolean isCompletionItemDocumentationEnabled; - final private boolean isHoverDocumentationEnabled; - final private boolean snippetAutoIndent; - final private boolean isSignatureHelpDocumentationEnabled; - final private boolean isCompletionSnippetsEnabled; - final private List semanticdbCompilerOptions; - - public PresentationCompilerConfig(Map symbolPrefixes, Optional completionCommand, - Optional parameterHintsCommand, String overrideDefFormat, boolean isDefaultSymbolPrefixes, - boolean isCompletionItemDetailEnabled, boolean isStripMarginOnTypeFormattingEnabled, - boolean isCompletionItemDocumentationEnabled, boolean isHoverDocumentationEnabled, - boolean snippetAutoIndent, boolean isSignatureHelpDocumentationEnabled, boolean isCompletionSnippetsEnabled, - List semanticdbCompilerOptions) { - this.symbolPrefixes = symbolPrefixes; - this.completionCommand = completionCommand; - this.parameterHintsCommand = parameterHintsCommand; - this.overrideDefFormat = overrideDefFormat; - this.isDefaultSymbolPrefixes = isDefaultSymbolPrefixes; - this.isCompletionItemDetailEnabled = isCompletionItemDetailEnabled; - this.isStripMarginOnTypeFormattingEnabled = isStripMarginOnTypeFormattingEnabled; - this.isCompletionItemDocumentationEnabled = isCompletionItemDocumentationEnabled; - this.isHoverDocumentationEnabled = isHoverDocumentationEnabled; - this.snippetAutoIndent = snippetAutoIndent; - this.isSignatureHelpDocumentationEnabled = isSignatureHelpDocumentationEnabled; - this.isCompletionSnippetsEnabled = isCompletionSnippetsEnabled; - this.semanticdbCompilerOptions = semanticdbCompilerOptions; - } - - public Map getSymbolPrefixes() { - return symbolPrefixes; - } - - public Optional getCompletionCommand() { - return completionCommand; - } - - public Optional getParameterHintsCommand() { - return parameterHintsCommand; - } - - public String getOverrideDefFormat() { - return overrideDefFormat; - } - - public boolean isDefaultSymbolPrefixes() { - return isDefaultSymbolPrefixes; - } - - public boolean isCompletionItemDetailEnabled() { - return isCompletionItemDetailEnabled; - } - - public boolean isStripMarginOnTypeFormattingEnabled() { - return isStripMarginOnTypeFormattingEnabled; - } - - public boolean isCompletionItemDocumentationEnabled() { - return isCompletionItemDocumentationEnabled; - } - - public boolean isHoverDocumentationEnabled() { - return isHoverDocumentationEnabled; - } - - public boolean isSnippetAutoIndent() { - return snippetAutoIndent; - } - - public boolean isSignatureHelpDocumentationEnabled() { - return isSignatureHelpDocumentationEnabled; - } - - public boolean isCompletionSnippetsEnabled() { - return isCompletionSnippetsEnabled; - } - - public List getSemanticdbCompilerOptions() { - return semanticdbCompilerOptions; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((symbolPrefixes == null) ? 0 : symbolPrefixes.hashCode()); - result = prime * result + ((completionCommand == null) ? 0 : completionCommand.hashCode()); - result = prime * result + ((parameterHintsCommand == null) ? 0 : parameterHintsCommand.hashCode()); - result = prime * result + ((overrideDefFormat == null) ? 0 : overrideDefFormat.hashCode()); - result = prime * result + (isDefaultSymbolPrefixes ? 1231 : 1237); - result = prime * result + (isCompletionItemDetailEnabled ? 1231 : 1237); - result = prime * result + (isStripMarginOnTypeFormattingEnabled ? 1231 : 1237); - result = prime * result + (isCompletionItemDocumentationEnabled ? 1231 : 1237); - result = prime * result + (isHoverDocumentationEnabled ? 1231 : 1237); - result = prime * result + (snippetAutoIndent ? 1231 : 1237); - result = prime * result + (isSignatureHelpDocumentationEnabled ? 1231 : 1237); - result = prime * result + (isCompletionSnippetsEnabled ? 1231 : 1237); - result = prime * result + ((semanticdbCompilerOptions == null) ? 0 : semanticdbCompilerOptions.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PresentationCompilerConfig other = (PresentationCompilerConfig) obj; - if (symbolPrefixes == null) { - if (other.symbolPrefixes != null) - return false; - } else if (!symbolPrefixes.equals(other.symbolPrefixes)) - return false; - if (completionCommand == null) { - if (other.completionCommand != null) - return false; - } else if (!completionCommand.equals(other.completionCommand)) - return false; - if (parameterHintsCommand == null) { - if (other.parameterHintsCommand != null) - return false; - } else if (!parameterHintsCommand.equals(other.parameterHintsCommand)) - return false; - if (overrideDefFormat == null) { - if (other.overrideDefFormat != null) - return false; - } else if (!overrideDefFormat.equals(other.overrideDefFormat)) - return false; - if (isDefaultSymbolPrefixes != other.isDefaultSymbolPrefixes) - return false; - if (isCompletionItemDetailEnabled != other.isCompletionItemDetailEnabled) - return false; - if (isStripMarginOnTypeFormattingEnabled != other.isStripMarginOnTypeFormattingEnabled) - return false; - if (isCompletionItemDocumentationEnabled != other.isCompletionItemDocumentationEnabled) - return false; - if (isHoverDocumentationEnabled != other.isHoverDocumentationEnabled) - return false; - if (snippetAutoIndent != other.snippetAutoIndent) - return false; - if (isSignatureHelpDocumentationEnabled != other.isSignatureHelpDocumentationEnabled) - return false; - if (isCompletionSnippetsEnabled != other.isCompletionSnippetsEnabled) - return false; - if (semanticdbCompilerOptions == null) { - if (other.semanticdbCompilerOptions != null) - return false; - } else if (!semanticdbCompilerOptions.equals(other.semanticdbCompilerOptions)) - return false; - return true; - } - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ReporterContext.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ReporterContext.java deleted file mode 100644 index bb6c4bacb41..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ReporterContext.java +++ /dev/null @@ -1,4 +0,0 @@ -package scala.meta.internal.telemetry; - -public interface ReporterContext { -} diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ReporterContextUnion.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ReporterContextUnion.java deleted file mode 100644 index 8ee74e07cf8..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ReporterContextUnion.java +++ /dev/null @@ -1,69 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.Optional; - -public class ReporterContextUnion { - final private Optional metalsLSP; - final private Optional scalaPresentationCompiler; - - public ReporterContextUnion(Optional metalsLSP, - Optional scalaPresentationCompiler) { - this.metalsLSP = metalsLSP; - this.scalaPresentationCompiler = scalaPresentationCompiler; - } - - public ReporterContext get() { - if (metalsLSP.isPresent()) - return metalsLSP.get(); - if (scalaPresentationCompiler.isPresent()) - return scalaPresentationCompiler.get(); - throw new IllegalStateException("None of union values is defined"); - } - - public static ReporterContextUnion metalsLSP(MetalsLspContext ctx) { - return new ReporterContextUnion(Optional.of(ctx), Optional.empty()); - } - - public static ReporterContextUnion scalaPresentationCompiler(ScalaPresentationCompilerContext ctx) { - return new ReporterContextUnion(Optional.empty(), Optional.of(ctx)); - } - - public Optional getMetalsLSP() { - return metalsLSP; - } - - public Optional getScalaPresentationCompiler() { - return scalaPresentationCompiler; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((metalsLSP == null) ? 0 : metalsLSP.hashCode()); - result = prime * result + ((scalaPresentationCompiler == null) ? 0 : scalaPresentationCompiler.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ReporterContextUnion other = (ReporterContextUnion) obj; - if (metalsLSP == null) { - if (other.metalsLSP != null) - return false; - } else if (!metalsLSP.equals(other.metalsLSP)) - return false; - if (scalaPresentationCompiler == null) { - if (other.scalaPresentationCompiler != null) - return false; - } else if (!scalaPresentationCompiler.equals(other.scalaPresentationCompiler)) - return false; - return true; - } -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ScalaPresentationCompilerContext.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ScalaPresentationCompilerContext.java deleted file mode 100644 index bafb4998d20..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ScalaPresentationCompilerContext.java +++ /dev/null @@ -1,65 +0,0 @@ -package scala.meta.internal.telemetry; - -import java.util.List; - -public class ScalaPresentationCompilerContext implements ReporterContext { - final private String scalaVersion; - final private List options; - final private PresentationCompilerConfig config; - - public ScalaPresentationCompilerContext(String scalaVersion, List options, - PresentationCompilerConfig config) { - this.scalaVersion = scalaVersion; - this.options = options; - this.config = config; - } - - public String getScalaVersion() { - return scalaVersion; - } - - public List getOptions() { - return options; - } - - public PresentationCompilerConfig getConfig() { - return config; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((scalaVersion == null) ? 0 : scalaVersion.hashCode()); - result = prime * result + ((options == null) ? 0 : options.hashCode()); - result = prime * result + ((config == null) ? 0 : config.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ScalaPresentationCompilerContext other = (ScalaPresentationCompilerContext) obj; - if (scalaVersion == null) { - if (other.scalaVersion != null) - return false; - } else if (!scalaVersion.equals(other.scalaVersion)) - return false; - if (options == null) { - if (other.options != null) - return false; - } else if (!options.equals(other.options)) - return false; - if (config == null) { - if (other.config != null) - return false; - } else if (!config.equals(other.config)) - return false; - return true; - } -} diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ServiceEndpoint.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ServiceEndpoint.java deleted file mode 100644 index 71124ca7e82..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/ServiceEndpoint.java +++ /dev/null @@ -1,75 +0,0 @@ -package scala.meta.internal.telemetry; - -public class ServiceEndpoint { - final private String uri; - final private String method; - final private Class inputType; - final private Class outputType; - - public ServiceEndpoint(String method, String uri, Class inputType, Class outputType) { - this.uri = uri; - this.method = method; - this.inputType = inputType; - this.outputType = outputType; - } - - public String getUri() { - return uri; - } - - public String getMethod() { - return method; - } - - public Class getInputType() { - return inputType; - } - - public Class getOutputType() { - return outputType; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((uri == null) ? 0 : uri.hashCode()); - result = prime * result + ((method == null) ? 0 : method.hashCode()); - result = prime * result + ((inputType == null) ? 0 : inputType.hashCode()); - result = prime * result + ((outputType == null) ? 0 : outputType.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ServiceEndpoint other = (ServiceEndpoint) obj; - if (uri == null) { - if (other.uri != null) - return false; - } else if (!uri.equals(other.uri)) - return false; - if (method == null) { - if (other.method != null) - return false; - } else if (!method.equals(other.method)) - return false; - if (inputType == null) { - if (other.inputType != null) - return false; - } else if (!inputType.equals(other.inputType)) - return false; - if (outputType == null) { - if (other.outputType != null) - return false; - } else if (!outputType.equals(other.outputType)) - return false; - return true; - } - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/SystemInfo.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/SystemInfo.java deleted file mode 100644 index d2c3364fbac..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/SystemInfo.java +++ /dev/null @@ -1,63 +0,0 @@ -package scala.meta.internal.telemetry; - -public class SystemInfo { - final private String architecture; - final private String name; - final private String version; - - public SystemInfo(String architecture, String name, String version) { - this.architecture = architecture; - this.name = name; - this.version = version; - } - - public String getArchitecture() { - return architecture; - } - - public String getName() { - return name; - } - - public String getVersion() { - return version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((architecture == null) ? 0 : architecture.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SystemInfo other = (SystemInfo) obj; - if (architecture == null) { - if (other.architecture != null) - return false; - } else if (!architecture.equals(other.architecture)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (version == null) { - if (other.version != null) - return false; - } else if (!version.equals(other.version)) - return false; - return true; - } - -} \ No newline at end of file diff --git a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/TelemetryService.java b/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/TelemetryService.java deleted file mode 100644 index b5e7206014a..00000000000 --- a/telemetry-interfaces/src/main/java/scala/meta/internal/telemetry/TelemetryService.java +++ /dev/null @@ -1,14 +0,0 @@ -package scala.meta.internal.telemetry; - -public interface TelemetryService { - - void sendErrorReport(ErrorReport report); - - static final ServiceEndpoint SendErrorReportEndpoint = new ServiceEndpoint<>("POST", - "/v1/telemetry/sendErrorReport", ErrorReport.class, Void.class); - - void sendCrashReport(CrashReport report); - - static final ServiceEndpoint SendCrashReportEndpoint = new ServiceEndpoint<>("POST", - "/v1/telemetry/sendCrashReport", CrashReport.class, Void.class); -} diff --git a/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Environment.scala b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Environment.scala new file mode 100644 index 00000000000..d40ae39d38a --- /dev/null +++ b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Environment.scala @@ -0,0 +1,21 @@ +package scala.meta.internal.telemetry + + + +case class Environment(java: JavaInfo, system: SystemInfo) +object Environment { + val java: JavaInfo = JavaInfo( + System.getProperty("java.version", "unknown"), + System.getProperty("java.vendor", "unknown") + ) + val system: SystemInfo = SystemInfo( + System.getProperty("os.arch", "unknown"), + System.getProperty("os.name", "unknown"), + System.getProperty("os.version", "unknown") + ) + + val instance: Environment = Environment(java, system) +} + +case class JavaInfo(version: String, distribution: String) +case class SystemInfo(architecture: String, name: String, version: String) \ No newline at end of file diff --git a/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Exception.scala b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Exception.scala new file mode 100644 index 00000000000..a1d0fd9319e --- /dev/null +++ b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Exception.scala @@ -0,0 +1,22 @@ +package scala.meta.internal.telemetry + + +case class ExceptionSummary(exception: String, stacktrace: List[StackTraceElement]) + +case class StackTraceElement(fullyQualifiedName: String, methodName: String, errorFile: String, errorLine: Int) + +object ExceptionSummary { + def from(e: Throwable, sanitize: String => String): ExceptionSummary = { + val stackTraceElements = e.getStackTrace().toList.flatMap(element => { + for { + className <- Option(element.getClassName) + methodName <- Option(element.getMethodName) + errorLine <- Option(element.getLineNumber) + errorFile <- Option(element.getFileName) + } yield StackTraceElement(sanitize(className), sanitize(methodName), sanitize(errorFile), errorLine) + }) + + ExceptionSummary(sanitize(e.getMessage), stackTraceElements) + } + +} diff --git a/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/MetalsConfiguration.scala b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/MetalsConfiguration.scala new file mode 100644 index 00000000000..446387eebb3 --- /dev/null +++ b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/MetalsConfiguration.scala @@ -0,0 +1,31 @@ +package scala.meta.internal.telemetry + + +case class MetalsUserConfiguration( + symbolPrefixes: Map[String, String], + bloopSbtAlreadyInstalled: Boolean, + bloopVersion: Option[String], + bloopJvmProperties: List[String], + ammoniteJvmProperties: List[String], + superMethodLensesEnabled: Boolean, + showInferredType: Option[String], + showImplicitArguments: Boolean, + showImplicitConversionsAndClasses: Boolean, + enableStripMarginOnTypeFormatting: Boolean, + enableIndentOnPaste: Boolean, + enableSemanticHighlighting: Boolean, + excludedPackages: List[String], + fallbackScalaVersion: Option[String], + testUserInterface: String +) + +case class MetalsServerConfiguration( + executeClientCommand: String, + snippetAutoIndent: Boolean, + isHttpEnabled: Boolean, + isInputBoxEnabled: Boolean, + askToReconnect: Boolean, + allowMultilineStringFormatting: Boolean, + compilers: PresentationCompilerConfig +) + diff --git a/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/PresentationCompilerConfig.scala b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/PresentationCompilerConfig.scala new file mode 100644 index 00000000000..4900fed86d0 --- /dev/null +++ b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/PresentationCompilerConfig.scala @@ -0,0 +1,18 @@ +package scala.meta.internal.telemetry + + +case class PresentationCompilerConfig( + symbolPrefixes: Map[String, String], + completionCommand: Option[String], + parameterHintsCommand: Option[String], + overrideDefFormat: String, + isDefaultSymbolPrefixes: Boolean, + isCompletionItemDetailEnabled: Boolean, + isStripMarginOnTypeFormattingEnabled: Boolean, + isCompletionItemDocumentationEnabled: Boolean, + isHoverDocumentationEnabled: Boolean, + snippetAutoIndent: Boolean, + isSignatureHelpDocumentationEnabled: Boolean, + isCompletionSnippetsEnabled: Boolean, + semanticdbCompilerOptions: List[String] +) diff --git a/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/ReporterContext.scala b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/ReporterContext.scala new file mode 100644 index 00000000000..b0d5d1dbb04 --- /dev/null +++ b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/ReporterContext.scala @@ -0,0 +1,19 @@ +package scala.meta.internal.telemetry + +sealed trait ReporterContext +case class ScalaPresentationCompilerContext( + scalaVersion: String, + options: List[String], + config: PresentationCompilerConfig +) extends ReporterContext + +case class MetalsLspContext( + metalsVersion: String, + userConfig: MetalsUserConfiguration, + serverConfig: MetalsServerConfiguration, + clientInfo: MetalsClientInfo, + buildServerConnections: List[BuildServerConnection] +) extends ReporterContext + +case class MetalsClientInfo(name: Option[String], version: Option[String]) +case class BuildServerConnection(name: String, version: String, isMain: Boolean) \ No newline at end of file diff --git a/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Reports.scala b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Reports.scala new file mode 100644 index 00000000000..c53b66a48aa --- /dev/null +++ b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/Reports.scala @@ -0,0 +1,31 @@ +package scala.meta.internal.telemetry + +import com.github.plokhotnyuk.jsoniter_scala.core._ +import com.github.plokhotnyuk.jsoniter_scala.macros._ + +case class CrashReport( + error: ExceptionSummary, + componentName: String, + env: Environment = Environment.instance, + componentVersion: Option[String] = None, + reporterContext: Option[ReporterContext] = None +) + +object CrashReport { + implicit val codec: JsonValueCodec[CrashReport] = JsonCodecMaker.make +} + +case class ErrorReport( + name: String, + reporterName: String, + reporterContext: ReporterContext, + env: Environment = Environment.instance, + id: Option[String] = None, + text: Option[String] = None, + error: Option[ExceptionSummary] = None, +) + +object ErrorReport { + implicit val codec: JsonValueCodec[ErrorReport] = JsonCodecMaker.make +} + diff --git a/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/TelemetryService.scala b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/TelemetryService.scala new file mode 100644 index 00000000000..87621928321 --- /dev/null +++ b/telemetry-interfaces/src/main/scala/scala/meta/internal/telemetry/TelemetryService.scala @@ -0,0 +1,23 @@ +package scala.meta.internal.telemetry + +import com.github.plokhotnyuk.jsoniter_scala.core._ +import scala.util.Try + +class FireAndForgetEndpoint[In: JsonValueCodec](val method: String, val uri: String) { + def encodeInput(request: In): String = writeToString(request) + def decodeInput(request: String): Try[In] = Try { readFromString(request) } +} + +// This will be migrated to tapir endpoints in the next Commit +object TelemetryService { + val sendErrorReportEndpoint = new FireAndForgetEndpoint[ErrorReport]("POST", "/v1/telemetry/sendErrorReport") + val sendCrashReportEndpoint = new FireAndForgetEndpoint[CrashReport]("POST", "/v1/telemetry/sendCrashReport") +} + +trait TelemetryService { + def sendErrorReport(errorReport: ErrorReport): Unit + def sendCrashReport(crashReport: CrashReport): Unit +} + + + diff --git a/tests/unit/src/test/scala/tests/telemetry/SampleReports.scala b/tests/unit/src/test/scala/tests/telemetry/SampleReports.scala index d556799f9ca..ee2b27dd8c2 100644 --- a/tests/unit/src/test/scala/tests/telemetry/SampleReports.scala +++ b/tests/unit/src/test/scala/tests/telemetry/SampleReports.scala @@ -1,6 +1,5 @@ package tests.telemetry -import java.util.Optional import java.{util => ju} import scala.util.Random.nextBoolean @@ -13,130 +12,94 @@ object SampleReports { private case class MapControl(setEmpty: Boolean) private case class ListControl(setEmpty: Boolean) - private def optional[T](value: => T)(implicit - ctrl: OptionalControl - ): Optional[T] = - if (ctrl.setEmpty) Optional.empty() else Optional.of(value) - private def maybeEmptyMap[K, V](values: (K, V)*)(implicit - ctrl: MapControl - ): ju.Map[K, V] = - if (ctrl.setEmpty) ju.Collections.emptyMap else values.toMap.asJava - private def maybeEmptyList[T](values: T*)(implicit - ctrl: ListControl - ): ju.List[T] = - if (ctrl.setEmpty) ju.Collections.emptyList else values.asJava - - private def reportOf(ctx: telemetry.ReporterContextUnion)(implicit - opt: OptionalControl, - list: ListControl, - ): telemetry.ErrorReport = new telemetry.ErrorReport( - "name", - optional("text"), - optional("id"), - optional( - new telemetry.ExceptionSummary( - maybeEmptyList("ExceptionType"), - "stacktrace", - ) - ), - "reporterName", - ctx, - new telemetry.Environment( - new telemetry.JavaInfo("version", "distiribution"), - new telemetry.SystemInfo("arch", "name", "version"), - ), - ) + private def reportOf(ctx: telemetry.ReporterContext): telemetry.ErrorReport = + new telemetry.ErrorReport( + name = "name", + text = Some("text"), + reporterContext = ctx, + id = Some("id"), + error = Some( + telemetry.ExceptionSummary( + "ExceptionType", + List(telemetry.StackTraceElement("fullyQualifiedName", "methodName", "fileName", 0)), + ) + ), + reporterName = "reporterName", + env = telemetry.Environment( + new telemetry.JavaInfo("version", "distiribution"), + new telemetry.SystemInfo("arch", "name", "version"), + ), + ) - private def presentationCompilerConfig()(implicit - opt: OptionalControl, - map: MapControl, - list: ListControl, - ) = new telemetry.PresentationCompilerConfig( - maybeEmptyMap("symbol" -> "prefix"), - optional("command"), - optional("parameterHints"), - "overrideDefFormat", - nextBoolean, - nextBoolean, - nextBoolean, - nextBoolean, - nextBoolean, - nextBoolean, - nextBoolean, - nextBoolean, - maybeEmptyList("semanticDbOpts"), - ) + private def presentationCompilerConfig() = + new telemetry.PresentationCompilerConfig( + Map.from(List("symbol" -> "prefix")), + Some("command"), + Some("parameterHints"), + "overrideDefFormat", + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + List("semanticDbOpts"), + ) - def metalsLspReport( - emptyOptionals: Boolean = false, - emptyLists: Boolean = false, - emptyMaps: Boolean = false, - ): telemetry.ErrorReport = { - implicit val ctrl: OptionalControl = OptionalControl(!emptyOptionals) - implicit val map: MapControl = MapControl(!emptyMaps) - implicit val list: ListControl = ListControl(!emptyLists) + def metalsLspReport(): telemetry.ErrorReport = { reportOf( - telemetry.ReporterContextUnion.metalsLSP( - new telemetry.MetalsLspContext( - "metalsVersion", - new telemetry.MetalsUserConfiguration( - maybeEmptyMap("symbol" -> "prefix"), - nextBoolean(), - optional("bloopVersion"), - maybeEmptyList("props"), - maybeEmptyList("ammoniteProps"), - nextBoolean(), - optional("inferedTypes"), - nextBoolean(), - nextBoolean(), - nextBoolean(), - nextBoolean(), - nextBoolean(), - maybeEmptyList("package.name"), - optional("fallback version"), - "testUserInterface", - ), - new telemetry.MetalsServerConfiguration( - "clientCommand", - nextBoolean(), - nextBoolean(), - nextBoolean(), - nextBoolean(), - nextBoolean(), - presentationCompilerConfig, - ), - new telemetry.MetalsClientInfo( - optional("name"), - optional("version"), - ), - maybeEmptyList( - new telemetry.BuildServerConnection( - "connection.name", - "connection.version", - util.Random.nextBoolean(), - ) - ), - ) + telemetry.MetalsLspContext( + "metalsVersion", + telemetry.MetalsUserConfiguration( + Map.from(List("symbol" -> "prefix")), + nextBoolean(), + Some("bloopVersion"), + List("props"), + List("ammoniteProps"), + nextBoolean(), + Some("inferedTypes"), + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + List("package.name"), + Some("fallback version"), + "testUserInterface", + ), + telemetry.MetalsServerConfiguration( + "clientCommand", + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + nextBoolean(), + presentationCompilerConfig, + ), + telemetry.MetalsClientInfo( + Some("name"), + Some("version"), + ), + List( + telemetry.BuildServerConnection( + "connection.name", + "connection.version", + util.Random.nextBoolean(), + ) + ), ) ) } - def scalaPresentationCompilerReport( - emptyOptionals: Boolean = false, - emptyLists: Boolean = false, - emptyMaps: Boolean = false, - ): telemetry.ErrorReport = { - implicit val ctrl: OptionalControl = OptionalControl(!emptyOptionals) - implicit val map: MapControl = MapControl(!emptyMaps) - implicit val list: ListControl = ListControl(!emptyLists) + def scalaPresentationCompilerReport(): telemetry.ErrorReport = { reportOf( - telemetry.ReporterContextUnion.scalaPresentationCompiler( - new telemetry.ScalaPresentationCompilerContext( - "scalaVersion", - maybeEmptyList("options", "othersOptions"), - presentationCompilerConfig(), - ) + new telemetry.ScalaPresentationCompilerContext( + "scalaVersion", + List("options", "othersOptions"), + presentationCompilerConfig(), ) ) } diff --git a/tests/unit/src/test/scala/tests/telemetry/SerializationSuite.scala b/tests/unit/src/test/scala/tests/telemetry/SerializationSuite.scala deleted file mode 100644 index bcc2e6fdf3c..00000000000 --- a/tests/unit/src/test/scala/tests/telemetry/SerializationSuite.scala +++ /dev/null @@ -1,38 +0,0 @@ -package tests.telemetry - -import scala.meta.internal.telemetry - -import tests.BaseSuite - -/* Test checking correctness of to/from JSON serialization for telemetry model. - It's purpouse is to check if Optional[T] fields and Java collections are correctly serialized. - Optional fields would be used to evolve the model in backward compatible way, however by default GSON can initialize Optional fields to null if they're missing in the json fields. - */ -class SerializationSuite extends BaseSuite { - - test("serialization") { - def withConfig(emptyCollections: Boolean) = { - Seq( - SampleReports.metalsLspReport( - emptyOptionals = emptyCollections, - emptyLists = emptyCollections, - emptyMaps = emptyCollections, - ), - SampleReports.scalaPresentationCompilerReport( - emptyOptionals = emptyCollections, - emptyLists = emptyCollections, - emptyMaps = emptyCollections, - ), - ).foreach { report => - val codec = telemetry.GsonCodecs.gson - assert(!codec.serializeNulls(), "Codec should not serialize nulls") - val json = codec.toJson(report) - val fromJson = codec.fromJson(json, report.getClass()) - assertEquals(report, fromJson) - } - } - - withConfig(emptyCollections = false) - withConfig(emptyCollections = true) - } -} diff --git a/tests/unit/src/test/scala/tests/telemetry/TelemetryReporterSuite.scala b/tests/unit/src/test/scala/tests/telemetry/TelemetryReporterSuite.scala index 02c70b3812f..47ec4b0f320 100644 --- a/tests/unit/src/test/scala/tests/telemetry/TelemetryReporterSuite.scala +++ b/tests/unit/src/test/scala/tests/telemetry/TelemetryReporterSuite.scala @@ -7,6 +7,7 @@ import java.nio.file.Path import java.util.Optional import scala.collection.mutable +import scala.concurrent.ExecutionContext.Implicits.global import scala.util.control.NonFatal import scala.meta.internal.jdk.CollectionConverters._ @@ -35,11 +36,6 @@ class TelemetryReporterSuite extends BaseSuite { test("default telemetry level") { def getDefault = metals.TelemetryLevel.default assertEquals(metals.TelemetryLevel.Off, getDefault) - sys.props - .get(metals.TelemetryLevel.SystemPropertyKey) - .fold(fail("Expected telemetry level system property to be overriden")) { - assertEquals(_, metals.TelemetryLevel.Off.stringValue) - } } // Remote telemetry reporter should be treated as best effort, ensure that logging @@ -48,9 +44,8 @@ class TelemetryReporterSuite extends BaseSuite { telemetryClientConfig = TelemetryClient.Config.default.copy(serverHost = "https://not.existing.endpoint.for.metals.tests:8081" ), - telemetryLevel = () => metals.TelemetryLevel.All, - reporterContext = () => - SampleReports.metalsLspReport().getReporterContext.getMetalsLSP.get(), + telemetryLevel = () => metals.TelemetryLevel.Full, + reporterContext = () => SampleReports.metalsLspReport().reporterContext, sanitizers = new TelemetryReportContext.Sanitizers(None, None), ) @@ -71,11 +66,11 @@ class TelemetryReporterSuite extends BaseSuite { reporterCtx <- Seq( SampleReports.metalsLspReport(), SampleReports.scalaPresentationCompilerReport(), - ).map(_.getReporterContext().get()) + ).map(_.reporterContext) reporter = new TelemetryReportContext( telemetryClientConfig = TelemetryClient.Config.default .copy(serverHost = serverEndpoint), - telemetryLevel = () => metals.TelemetryLevel.All, + telemetryLevel = () => metals.TelemetryLevel.Full, reporterContext = () => reporterCtx, sanitizers = new TelemetryReportContext.Sanitizers( None, @@ -85,7 +80,8 @@ class TelemetryReporterSuite extends BaseSuite { } { val createdReport = simpleReport(reporterCtx.toString()) reporter.incognito.create(createdReport) - val received = ctx.errors.filter(_.getId().asScala == createdReport.id) + Thread.sleep(5000) // wait for the server to receive the event + val received = ctx.errors.filter(_.id == createdReport.id.asScala) assert(received.nonEmpty, "Not received matching id") assert(received.size == 1, "Found more then 1 received event") } @@ -113,14 +109,12 @@ object MockTelemetryServer { val baseHandler = path() .withEndpoint( - TelemetryService.SendErrorReportEndpoint, - defaultResponse = null.asInstanceOf[Void], + TelemetryService.sendErrorReportEndpoint, _.errors, ) .withEndpoint( - TelemetryService.SendCrashReportEndpoint, - defaultResponse = null.asInstanceOf[Void], - _.crashes, /*unused*/ + TelemetryService.sendCrashReportEndpoint, + _.crashes, ) Undertow.builder .addHttpListener(port, host) @@ -129,41 +123,34 @@ object MockTelemetryServer { } implicit class EndpointOps(private val handler: PathHandler) extends AnyVal { - def withEndpoint[I, O]( - endpoint: ServiceEndpoint[I, O], - defaultResponse: O, - eventCollectionsSelector: Context => mutable.ListBuffer[I], + def withEndpoint[In]( + endpoint: FireAndForgetEndpoint[In], + eventCollectionsSelector: Context => mutable.ListBuffer[In], )(implicit ctx: Context): PathHandler = handler.addExactPath( - endpoint.getUri(), + endpoint.uri, new BlockingHandler( - new SimpleHttpHandler[I, O]( + new SimpleHttpHandler[In]( endpoint, - defaultResponse, eventCollectionsSelector(ctx), ) ), ) } - private class SimpleHttpHandler[I, O]( - endpoint: ServiceEndpoint[I, O], - response: O, - receivedEvents: mutable.ListBuffer[I], + private class SimpleHttpHandler[In]( + endpoint: FireAndForgetEndpoint[In], + receivedEvents: mutable.ListBuffer[In], ) extends HttpHandler { override def handleRequest(exchange: HttpServerExchange): Unit = { exchange.getRequestReceiver().receiveFullString { (exchange: HttpServerExchange, json: String) => - receivedEvents += GsonCodecs.gson - .fromJson(json, endpoint.getInputType()) + receivedEvents += endpoint.decodeInput(json).get exchange .getResponseHeaders() .put(Headers.CONTENT_TYPE, "application/json") exchange .getResponseSender() - .send( - GsonCodecs.gson - .toJson(response, endpoint.getOutputType()) - ) + .send("") } } }