Documentation for functions and locals (#4029)

Add documentation for functions and locals to suggestions database.
This commit is contained in:
Dmitry Bushev 2023-01-10 19:59:53 +03:00 committed by GitHub
parent bc66753627
commit 2cd880f43d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 396 additions and 96 deletions

View File

@ -602,6 +602,15 @@ interface Function {
/** The scope where the function is defined. */ /** The scope where the function is defined. */
scope: SuggestionEntryScope; scope: SuggestionEntryScope;
/** The documentation string. */
documentation?: string;
/** The rendered HTML of the documentation string. */
documentationHtml?: string;
/** The documentation string divided into sections. */
documentationSections?: DocSection[];
} }
interface Local { interface Local {
@ -619,6 +628,15 @@ interface Local {
/** The scope where the value is defined. */ /** The scope where the value is defined. */
scope: SuggestionEntryScope; scope: SuggestionEntryScope;
/** The documentation string. */
documentation?: string;
/** The rendered HTML of the documentation string. */
documentationHtml?: string;
/** The documentation string divided into sections. */
documentationSections?: DocSection[];
} }
``` ```
@ -940,6 +958,11 @@ interface Modify {
*/ */
documentation?: FieldUpdate<String>; documentation?: FieldUpdate<String>;
/**
* New documentation sections.
*/
documentationSections?: FieldUpdate<DocSection[]>;
/** /**
* The scope to update. * The scope to update.
*/ */

View File

@ -408,20 +408,22 @@ object SearchProtocol {
* @param returnType the return type to update * @param returnType the return type to update
* @param documentation the documentation string to update * @param documentation the documentation string to update
* @param documentationHtml the HTML documentation to update * @param documentationHtml the HTML documentation to update
* @param documentationSections the documentation sections to update
* @param scope the scope to update * @param scope the scope to update
* @param reexport the module reexporting the suggestion * @param reexport the module reexporting the suggestion
*/ */
case class Modify( case class Modify(
id: SuggestionId, id: SuggestionId,
externalId: Option[FieldUpdate[Suggestion.ExternalId]] = None, externalId: Option[FieldUpdate[Suggestion.ExternalId]] = None,
arguments: Option[Seq[SuggestionArgumentUpdate]] = None, arguments: Option[Seq[SuggestionArgumentUpdate]] = None,
module: Option[FieldUpdate[String]] = None, module: Option[FieldUpdate[String]] = None,
selfType: Option[FieldUpdate[String]] = None, selfType: Option[FieldUpdate[String]] = None,
returnType: Option[FieldUpdate[String]] = None, returnType: Option[FieldUpdate[String]] = None,
documentation: Option[FieldUpdate[String]] = None, documentation: Option[FieldUpdate[String]] = None,
documentationHtml: Option[FieldUpdate[String]] = None, documentationHtml: Option[FieldUpdate[String]] = None,
scope: Option[FieldUpdate[Suggestion.Scope]] = None, documentationSections: Option[FieldUpdate[Seq[DocSection]]] = None,
reexport: Option[FieldUpdate[String]] = None scope: Option[FieldUpdate[Suggestion.Scope]] = None,
reexport: Option[FieldUpdate[String]] = None
) extends SuggestionsDatabaseUpdate ) extends SuggestionsDatabaseUpdate
implicit val decoder: Decoder[SuggestionsDatabaseUpdate] = implicit val decoder: Decoder[SuggestionsDatabaseUpdate] =

View File

@ -591,8 +591,11 @@ final class SuggestionsHandler(
externalId = m.externalId.map(fieldUpdateOption), externalId = m.externalId.map(fieldUpdateOption),
arguments = m.arguments.map(_.map(toApiArgumentAction)), arguments = m.arguments.map(_.map(toApiArgumentAction)),
returnType = m.returnType.map(fieldUpdate), returnType = m.returnType.map(fieldUpdate),
scope = m.scope.map(fieldUpdate),
documentation = m.documentation.map(fieldUpdateOption), documentation = m.documentation.map(fieldUpdateOption),
scope = m.scope.map(fieldUpdate) documentationSections = m.documentation.map(
fieldUpdateMapOption(docSectionsBuilder.build)
)
) )
} }
} }
@ -625,7 +628,7 @@ final class SuggestionsHandler(
/** Construct the field update object from an optional value. /** Construct the field update object from an optional value.
* *
* @param value the optional value * @param value the optional value
* @return the field update object representint the value update * @return the field update object representing the value update
*/ */
private def fieldUpdateOption[A](value: Option[A]): FieldUpdate[A] = private def fieldUpdateOption[A](value: Option[A]): FieldUpdate[A] =
value match { value match {
@ -633,6 +636,20 @@ final class SuggestionsHandler(
case None => FieldUpdate(FieldAction.Remove, None) case None => FieldUpdate(FieldAction.Remove, None)
} }
/** Construct the field update object from an optional value.
*
* @param f the mapping function
* @param value the optional value
* @return the field update object representing the value update
*/
private def fieldUpdateMapOption[A, B](
f: A => B
)(value: Option[A]): FieldUpdate[B] =
value match {
case Some(value) => FieldUpdate(FieldAction.Set, Some(f(value)))
case None => FieldUpdate(FieldAction.Remove, None)
}
/** Construct the field update object from and update value. /** Construct the field update object from and update value.
* *
* @param value the update value * @param value the update value
@ -731,8 +748,13 @@ final class SuggestionsHandler(
val docSections = conversion.documentation.map(docSectionsBuilder.build) val docSections = conversion.documentation.map(docSectionsBuilder.build)
conversion.copy(documentationSections = docSections) conversion.copy(documentationSections = docSections)
case _: Suggestion.Function => suggestion case function: Suggestion.Function =>
case _: Suggestion.Local => suggestion val docSections = function.documentation.map(docSectionsBuilder.build)
function.copy(documentationSections = docSections)
case local: Suggestion.Local =>
val docSections = local.documentation.map(docSectionsBuilder.build)
local.copy(documentationSections = docSections)
} }
} }

View File

@ -87,7 +87,10 @@ object Suggestions {
), ),
returnType = "IO", returnType = "IO",
scope = scope =
Suggestion.Scope(Suggestion.Position(1, 9), Suggestion.Position(1, 22)) Suggestion.Scope(Suggestion.Position(1, 9), Suggestion.Position(1, 22)),
documentation = Some("My Function"),
documentationHtml = None,
documentationSections = Some(docSectionsBuilder.build("My Function"))
) )
val local: Suggestion.Local = Suggestion.Local( val local: Suggestion.Local = Suggestion.Local(
@ -96,7 +99,8 @@ object Suggestions {
name = "x", name = "x",
returnType = "Number", returnType = "Number",
scope = scope =
Suggestion.Scope(Suggestion.Position(21, 0), Suggestion.Position(89, 0)) Suggestion.Scope(Suggestion.Position(21, 0), Suggestion.Position(89, 0)),
documentation = None
) )
val methodOnAny: Suggestion.Method = Suggestion.Method( val methodOnAny: Suggestion.Method = Suggestion.Method(

View File

@ -307,7 +307,14 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
"line" : 1, "line" : 1,
"character" : 22 "character" : 22
} }
} },
"documentation" : "My Function",
"documentationSections" : [
{
"type" : "paragraph",
"body" : "My Function"
}
]
} }
} }
], ],
@ -503,7 +510,14 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
"line" : 1, "line" : 1,
"character" : 22 "character" : 22
} }
} },
"documentation" : "My Function",
"documentationSections" : [
{
"type" : "paragraph",
"body" : "My Function"
}
]
} }
}, },
{ {

View File

@ -41,10 +41,11 @@ import java.util.UUID
) )
sealed trait Suggestion extends ToLogString { sealed trait Suggestion extends ToLogString {
def externalId: Option[Suggestion.ExternalId] def externalId: Option[Suggestion.ExternalId]
def module: String def module: String
def name: String def name: String
def returnType: String def returnType: String
def documentation: Option[String]
} }
object Suggestion { object Suggestion {
@ -121,20 +122,6 @@ object Suggestion {
} }
} }
/** Documentation extractor */
object Documentation {
def apply(suggestion: Suggestion): Option[String] =
suggestion match {
case module: Module => module.documentation
case tpe: Type => tpe.documentation
case constructor: Constructor => constructor.documentation
case method: Method => method.documentation
case conv: Conversion => conv.documentation
case _: Function => None
case _: Local => None
}
}
/** An argument of an atom or a function. /** An argument of an atom or a function.
* *
* @param name the argument name * @param name the argument name
@ -185,6 +172,7 @@ object Suggestion {
* @param module the fully qualified module name * @param module the fully qualified module name
* @param documentation the documentation string * @param documentation the documentation string
* @param documentationHtml the documentation rendered as HTML * @param documentationHtml the documentation rendered as HTML
* @param documentationSections the documentation parsed into sections
* @param reexport the module re-exporting this module * @param reexport the module re-exporting this module
*/ */
case class Module( case class Module(
@ -222,6 +210,7 @@ object Suggestion {
* @param parentType qualified name of the parent type * @param parentType qualified name of the parent type
* @param documentation the documentation string * @param documentation the documentation string
* @param documentationHtml the documentation rendered as HTML * @param documentationHtml the documentation rendered as HTML
* @param documentationSections the documentation parsed into sections
* @param reexport the module re-exporting this atom * @param reexport the module re-exporting this atom
*/ */
case class Type( case class Type(
@ -261,6 +250,7 @@ object Suggestion {
* @param returnType the type of an atom * @param returnType the type of an atom
* @param documentation the documentation string * @param documentation the documentation string
* @param documentationHtml the documentation rendered as HTML * @param documentationHtml the documentation rendered as HTML
* @param documentationSections the documentation parsed into sections
* @param reexport the module re-exporting this atom * @param reexport the module re-exporting this atom
*/ */
case class Constructor( case class Constructor(
@ -300,6 +290,7 @@ object Suggestion {
* @param isStatic the flag indicating whether a method is static or instance * @param isStatic the flag indicating whether a method is static or instance
* @param documentation the documentation string * @param documentation the documentation string
* @param documentationHtml the documentation rendered as HTML * @param documentationHtml the documentation rendered as HTML
* @param documentationSections the documentation parsed into sections
* @param reexport the module re-exporting this method * @param reexport the module re-exporting this method
*/ */
case class Method( case class Method(
@ -340,6 +331,7 @@ object Suggestion {
* @param returnType the return type of a conversion * @param returnType the return type of a conversion
* @param documentation the documentation string * @param documentation the documentation string
* @param documentationHtml the documentation rendered as HTML * @param documentationHtml the documentation rendered as HTML
* @param documentationSections the documentation parsed into sections
* @param reexport the module re-exporting this conversion * @param reexport the module re-exporting this conversion
*/ */
case class Conversion( case class Conversion(
@ -378,6 +370,9 @@ object Suggestion {
* @param arguments the function arguments * @param arguments the function arguments
* @param returnType the return type of a function * @param returnType the return type of a function
* @param scope the scope where the function is defined * @param scope the scope where the function is defined
* @param documentation the documentation string
* @param documentationHtml the documentation rendered as HTML
* @param documentationSections the documentation parsed into sections
*/ */
case class Function( case class Function(
externalId: Option[ExternalId], externalId: Option[ExternalId],
@ -385,7 +380,10 @@ object Suggestion {
name: String, name: String,
arguments: Seq[Argument], arguments: Seq[Argument],
returnType: String, returnType: String,
scope: Scope scope: Scope,
documentation: Option[String],
documentationHtml: Option[String] = None,
documentationSections: Option[List[DocSection]] = None
) extends Suggestion ) extends Suggestion
with ToLogString { with ToLogString {
@ -397,7 +395,8 @@ object Suggestion {
s"name=$name," + s"name=$name," +
s"arguments=${arguments.map(_.toLogString(shouldMask))}," + s"arguments=${arguments.map(_.toLogString(shouldMask))}," +
s"returnType=$returnType," + s"returnType=$returnType," +
s"scope=$scope" + s"scope=$scope," +
s"documentation=$documentation" +
")" ")"
} }
@ -408,13 +407,19 @@ object Suggestion {
* @param name the name of a value * @param name the name of a value
* @param returnType the type of a local value * @param returnType the type of a local value
* @param scope the scope where the value is defined * @param scope the scope where the value is defined
* @param documentation the documentation string
* @param documentationHtml the documentation rendered as HTML
* @param documentationSections the documentation parsed into sections
*/ */
case class Local( case class Local(
externalId: Option[ExternalId], externalId: Option[ExternalId],
module: String, module: String,
name: String, name: String,
returnType: String, returnType: String,
scope: Scope scope: Scope,
documentation: Option[String],
documentationHtml: Option[String] = None,
documentationSections: Option[List[DocSection]] = None
) extends Suggestion { ) extends Suggestion {
/** @inheritdoc */ /** @inheritdoc */
@ -424,7 +429,8 @@ object Suggestion {
s"module=$module," + s"module=$module," +
s"name=$name," + s"name=$name," +
s"returnType=$returnType," + s"returnType=$returnType," +
s"scope=$scope" + s"scope=$scope," +
s"documentation=$documentation" +
s")" s")"
} }
} }

View File

@ -360,10 +360,12 @@ class RuntimeStdlibTest
) )
) => ) =>
updates.toVector.foreach { update => updates.toVector.foreach { update =>
val docstring = Suggestion.Documentation(update.suggestion) update.suggestion.documentation.foreach { documentation =>
docstring.foreach( context.docsGenerator.generate(
context.docsGenerator.generate(_, update.suggestion.name) documentation,
) update.suggestion.name
)
}
} }
} }
} }

View File

@ -247,7 +247,8 @@ class RuntimeSuggestionUpdatesTest
Suggestion.Scope( Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(4, 16) Suggestion.Position(4, 16)
) ),
None
), ),
Api.SuggestionAction.Add() Api.SuggestionAction.Add()
), ),
@ -328,7 +329,8 @@ class RuntimeSuggestionUpdatesTest
Suggestion.Scope( Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(4, 16) Suggestion.Position(4, 16)
) ),
None
), ),
Api.SuggestionAction.Modify(scope = Api.SuggestionAction.Modify(scope =
Some( Some(
@ -351,7 +353,8 @@ class RuntimeSuggestionUpdatesTest
Suggestion.Scope( Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(5, 18) Suggestion.Position(5, 18)
) ),
None
), ),
Api.SuggestionAction.Add() Api.SuggestionAction.Add()
), ),
@ -429,7 +432,8 @@ class RuntimeSuggestionUpdatesTest
Suggestion.Scope( Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(5, 18) Suggestion.Position(5, 18)
) ),
None
), ),
Api.SuggestionAction.Modify(scope = Api.SuggestionAction.Modify(scope =
Some( Some(
@ -452,7 +456,8 @@ class RuntimeSuggestionUpdatesTest
Suggestion.Scope( Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(5, 18) Suggestion.Position(5, 18)
) ),
None
), ),
Api.SuggestionAction.Modify( Api.SuggestionAction.Modify(
returnType = Some(ConstantsGen.NUMBER), returnType = Some(ConstantsGen.NUMBER),
@ -540,7 +545,8 @@ class RuntimeSuggestionUpdatesTest
Suggestion.Scope( Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(6, 18) Suggestion.Position(6, 18)
) ),
None
), ),
Api.SuggestionAction.Modify(scope = Api.SuggestionAction.Modify(scope =
Some( Some(
@ -563,7 +569,8 @@ class RuntimeSuggestionUpdatesTest
Suggestion.Scope( Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(6, 18) Suggestion.Position(6, 18)
) ),
None
), ),
Api.SuggestionAction.Modify(scope = Api.SuggestionAction.Modify(scope =
Some( Some(
@ -808,7 +815,8 @@ class RuntimeSuggestionUpdatesTest
Suggestion.Scope( Suggestion.Scope(
Suggestion.Position(4, 6), Suggestion.Position(4, 6),
Suggestion.Position(8, 11) Suggestion.Position(8, 11)
) ),
None
), ),
Api.SuggestionAction.Add() Api.SuggestionAction.Add()
), ),

View File

@ -165,6 +165,7 @@ final class SuggestionBuilder[A: IndexedSource](
name, name,
args, args,
scope.location.get, scope.location.get,
doc,
typeSignature typeSignature
) )
val subforest = go( val subforest = go(
@ -181,6 +182,7 @@ final class SuggestionBuilder[A: IndexedSource](
module, module,
name.name, name.name,
scope.location.get, scope.location.get,
doc,
typeSignature typeSignature
) )
val subforest = go( val subforest = go(
@ -264,18 +266,20 @@ final class SuggestionBuilder[A: IndexedSource](
name: IR.Name, name: IR.Name,
args: Seq[IR.DefinitionArgument], args: Seq[IR.DefinitionArgument],
location: Location, location: Location,
doc: Option[String],
typeSignature: Option[TypeSignatures.Metadata] typeSignature: Option[TypeSignatures.Metadata]
): Suggestion.Function = { ): Suggestion.Function = {
val typeSig = buildTypeSignatureFromMetadata(typeSignature) val typeSig = buildTypeSignatureFromMetadata(typeSignature)
val (methodArgs, returnTypeDef) = val (methodArgs, returnTypeDef) =
buildFunctionArguments(args, typeSig) buildFunctionArguments(args, typeSig)
Suggestion.Function( Suggestion.Function(
externalId = externalId, externalId = externalId,
module = module.toString, module = module.toString,
name = name.name, name = name.name,
arguments = methodArgs, arguments = methodArgs,
returnType = buildReturnType(returnTypeDef), returnType = buildReturnType(returnTypeDef),
scope = buildScope(location) scope = buildScope(location),
documentation = doc
) )
} }
@ -285,16 +289,18 @@ final class SuggestionBuilder[A: IndexedSource](
module: QualifiedName, module: QualifiedName,
name: String, name: String,
location: Location, location: Location,
doc: Option[String],
typeSignature: Option[TypeSignatures.Metadata] typeSignature: Option[TypeSignatures.Metadata]
): Suggestion.Local = { ): Suggestion.Local = {
val typeSig = buildTypeSignatureFromMetadata(typeSignature) val typeSig = buildTypeSignatureFromMetadata(typeSignature)
val (_, returnTypeDef) = buildFunctionArguments(Seq(), typeSig) val (_, returnTypeDef) = buildFunctionArguments(Seq(), typeSig)
Suggestion.Local( Suggestion.Local(
externalId, externalId = externalId,
module.toString, module = module.toString,
name, name = name,
buildReturnType(returnTypeDef), returnType = buildReturnType(returnTypeDef),
buildScope(location) scope = buildScope(location),
documentation = doc
) )
} }

View File

@ -222,6 +222,9 @@ object SuggestionDiff {
if (e1.scope != e2.scope) { if (e1.scope != e2.scope) {
op = op.copy(scope = Some(e2.scope)) op = op.copy(scope = Some(e2.scope))
} }
if (e1.documentation != e2.documentation) {
op = op.copy(documentation = Some(e2.documentation))
}
Api.SuggestionUpdate(e1, op) Api.SuggestionUpdate(e1, op)
} }
@ -239,6 +242,9 @@ object SuggestionDiff {
if (e1.scope != e2.scope) { if (e1.scope != e2.scope) {
op = op.copy(scope = Some(e2.scope)) op = op.copy(scope = Some(e2.scope))
} }
if (e1.documentation != e2.documentation) {
op = op.copy(documentation = Some(e2.documentation))
}
Api.SuggestionUpdate(e1, op) Api.SuggestionUpdate(e1, op)
} }
} }

View File

@ -317,7 +317,11 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
"x", "x",
"Number", "Number",
Suggestion Suggestion
.Scope(Suggestion.Position(0, 9), Suggestion.Position(4, 9)) .Scope(
Suggestion.Position(0, 9),
Suggestion.Position(4, 9)
),
None
), ),
Vector() Vector()
), ),
@ -328,7 +332,11 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
"y", "y",
SuggestionBuilder.Any, SuggestionBuilder.Any,
Suggestion Suggestion
.Scope(Suggestion.Position(0, 9), Suggestion.Position(4, 9)) .Scope(
Suggestion.Position(0, 9),
Suggestion.Position(4, 9)
),
None
), ),
Vector() Vector()
) )
@ -939,7 +947,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(0, 6), Suggestion.Position(0, 6),
Suggestion.Position(2, 10) Suggestion.Position(2, 10)
) ),
None
), ),
Vector() Vector()
) )
@ -988,7 +997,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(0, 6), Suggestion.Position(0, 6),
Suggestion.Position(4, 10) Suggestion.Position(4, 10)
) ),
documentation = None
), ),
Vector( Vector(
Tree.Node( Tree.Node(
@ -1000,7 +1010,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(1, 11), Suggestion.Position(1, 11),
Suggestion.Position(3, 9) Suggestion.Position(3, 9)
) ),
documentation = None
), ),
Vector() Vector()
) )
@ -1049,7 +1060,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(0, 6), Suggestion.Position(0, 6),
Suggestion.Position(3, 10) Suggestion.Position(3, 10)
) ),
documentation = None
), ),
Vector() Vector()
) )
@ -1118,7 +1130,63 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(5, 10) Suggestion.Position(5, 10)
) ),
documentation = None
),
Vector()
)
)
)
)
)
}
"build function with documentation" in {
val code =
"""main =
| ## Foo documentation.
| foo a = a + 1
| foo 42
|""".stripMargin
val module = code.preprocessModule
build(code, module) shouldEqual Tree.Root(
Vector(
ModuleNode,
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "main",
arguments = Seq(),
selfType = "Unnamed.Test",
returnType = SuggestionBuilder.Any,
isStatic = true,
documentation = None
),
Vector(
Tree.Node(
Suggestion.Function(
externalId = None,
module = "Unnamed.Test",
name = "foo",
arguments = Seq(
Suggestion
.Argument(
"a",
SuggestionBuilder.Any,
false,
false,
None
)
),
returnType = SuggestionBuilder.Any,
scope = Suggestion.Scope(
Suggestion.Position(0, 6),
Suggestion.Position(3, 10)
),
documentation = Some(" Foo documentation.")
), ),
Vector() Vector()
) )
@ -1161,7 +1229,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(0, 6), Suggestion.Position(0, 6),
Suggestion.Position(2, 7) Suggestion.Position(2, 7)
) ),
documentation = None
), ),
Vector() Vector()
) )
@ -1206,7 +1275,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(0, 6), Suggestion.Position(0, 6),
Suggestion.Position(4, 7) Suggestion.Position(4, 7)
) ),
documentation = None
), ),
Vector( Vector(
Tree.Node( Tree.Node(
@ -1218,7 +1288,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(1, 9), Suggestion.Position(1, 9),
Suggestion.Position(3, 9) Suggestion.Position(3, 9)
) ),
documentation = None
), ),
Vector() Vector()
) )
@ -1264,7 +1335,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(0, 6), Suggestion.Position(0, 6),
Suggestion.Position(3, 7) Suggestion.Position(3, 7)
) ),
documentation = None
), ),
Vector() Vector()
) )
@ -1322,7 +1394,53 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(2, 6), Suggestion.Position(2, 6),
Suggestion.Position(5, 7) Suggestion.Position(5, 7)
) ),
documentation = None
),
Vector()
)
)
)
)
)
}
"build local with documentation" in {
val code =
"""main =
| ## This is foo.
| foo = 42
| foo
|""".stripMargin
val module = code.preprocessModule
build(code, module) shouldEqual Tree.Root(
Vector(
ModuleNode,
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "main",
arguments = Seq(),
selfType = "Unnamed.Test",
returnType = SuggestionBuilder.Any,
isStatic = true,
documentation = None
),
Vector(
Tree.Node(
Suggestion.Local(
externalId = None,
module = "Unnamed.Test",
name = "foo",
returnType = SuggestionBuilder.Any,
scope = Suggestion.Scope(
Suggestion.Position(0, 6),
Suggestion.Position(3, 7)
),
documentation = Some(" This is foo.")
), ),
Vector() Vector()
) )
@ -2187,7 +2305,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(0, 6), Suggestion.Position(0, 6),
Suggestion.Position(2, 28) Suggestion.Position(2, 28)
) ),
documentation = None
), ),
Vector() Vector()
) )
@ -2236,7 +2355,8 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(0, 6), Suggestion.Position(0, 6),
Suggestion.Position(2, 18) Suggestion.Position(2, 18)
) ),
documentation = None
), ),
Vector() Vector()
) )

View File

@ -77,21 +77,23 @@ object SuggestionRandom {
def nextSuggestionFunction(): Suggestion.Function = def nextSuggestionFunction(): Suggestion.Function =
Suggestion.Function( Suggestion.Function(
externalId = optional(UUID.randomUUID()), externalId = optional(UUID.randomUUID()),
module = "Test.Main", module = "Test.Main",
name = nextString(), name = nextString(),
arguments = Seq(), arguments = Seq(),
returnType = nextString(), returnType = nextString(),
scope = nextScope() scope = nextScope(),
documentation = optional(nextString())
) )
def nextSuggestionLocal(): Suggestion.Local = def nextSuggestionLocal(): Suggestion.Local =
Suggestion.Local( Suggestion.Local(
externalId = optional(UUID.randomUUID()), externalId = optional(UUID.randomUUID()),
module = "Test.Main", module = "Test.Main",
name = nextString(), name = nextString(),
returnType = nextString(), returnType = nextString(),
scope = nextScope() scope = nextScope(),
documentation = optional(nextString())
) )
def nextScope(): Suggestion.Scope = def nextScope(): Suggestion.Scope =

View File

@ -1129,7 +1129,17 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
reexport = reexport reexport = reexport
) )
row -> (firstArg +: args) row -> (firstArg +: args)
case Suggestion.Function(expr, module, name, args, returnType, scope) => case Suggestion.Function(
expr,
module,
name,
args,
returnType,
scope,
doc,
_,
_
) =>
val row = SuggestionRow( val row = SuggestionRow(
id = None, id = None,
externalIdLeast = expr.map(_.getLeastSignificantBits), externalIdLeast = expr.map(_.getLeastSignificantBits),
@ -1141,7 +1151,7 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
returnType = returnType, returnType = returnType,
parentType = None, parentType = None,
isStatic = false, isStatic = false,
documentation = None, documentation = doc,
scopeStartLine = scope.start.line, scopeStartLine = scope.start.line,
scopeStartOffset = scope.start.character, scopeStartOffset = scope.start.character,
scopeEndLine = scope.end.line, scopeEndLine = scope.end.line,
@ -1149,7 +1159,7 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
reexport = None reexport = None
) )
row -> args row -> args
case Suggestion.Local(expr, module, name, returnType, scope) => case Suggestion.Local(expr, module, name, returnType, scope, doc, _, _) =>
val row = SuggestionRow( val row = SuggestionRow(
id = None, id = None,
externalIdLeast = expr.map(_.getLeastSignificantBits), externalIdLeast = expr.map(_.getLeastSignificantBits),
@ -1161,7 +1171,7 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
returnType = returnType, returnType = returnType,
parentType = None, parentType = None,
isStatic = false, isStatic = false,
documentation = None, documentation = doc,
scopeStartLine = scope.start.line, scopeStartLine = scope.start.line,
scopeStartOffset = scope.start.character, scopeStartOffset = scope.start.character,
scopeEndLine = scope.end.line, scopeEndLine = scope.end.line,
@ -1288,7 +1298,10 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
suggestion.scopeEndLine, suggestion.scopeEndLine,
suggestion.scopeEndOffset suggestion.scopeEndOffset
) )
) ),
documentation = suggestion.documentation,
documentationHtml = None,
documentationSections = None
) )
case SuggestionKind.LOCAL => case SuggestionKind.LOCAL =>
Suggestion.Local( Suggestion.Local(
@ -1306,7 +1319,10 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
suggestion.scopeEndLine, suggestion.scopeEndLine,
suggestion.scopeEndOffset suggestion.scopeEndOffset
) )
) ),
documentation = suggestion.documentation,
documentationHtml = None,
documentationSections = None
) )
case k => case k =>
throw new NoSuchElementException(s"Unknown suggestion kind: $k") throw new NoSuchElementException(s"Unknown suggestion kind: $k")

View File

@ -675,6 +675,73 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
) )
} }
"update suggestion function documentation" taggedAs Retry in withRepo {
repo =>
val newDoc = "My awesome function!"
val action = for {
(v1, Seq(_, _, _, _, _, id1, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.tpe,
suggestion.constructor,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
)
(v2, id2) <- repo.update(
suggestion.function,
None,
None,
None,
Some(Some(newDoc)),
None,
None
)
s <- repo.select(id1.get)
} yield (v1, id1, v2, id2, s)
val (v1, id1, v2, id2, s) = Await.result(action, Timeout)
v1 should not equal v2
id1 shouldEqual id2
s shouldEqual Some(
suggestion.function.copy(documentation = Some(newDoc))
)
}
"update suggestion local documentation" taggedAs Retry in withRepo { repo =>
val newDoc = "Some stuff there"
val action = for {
(v1, Seq(_, _, _, _, _, _, id1)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.tpe,
suggestion.constructor,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
)
(v2, id2) <- repo.update(
suggestion.local,
None,
None,
None,
Some(Some(newDoc)),
None,
None
)
s <- repo.select(id1.get)
} yield (v1, id1, v2, id2, s)
val (v1, id1, v2, id2, s) = Await.result(action, Timeout)
v1 should not equal v2
id1 shouldEqual id2
s shouldEqual Some(
suggestion.local.copy(documentation = Some(newDoc))
)
}
"update suggestion removing documentation" taggedAs Retry in withRepo { "update suggestion removing documentation" taggedAs Retry in withRepo {
repo => repo =>
val action = for { val action = for {
@ -1898,8 +1965,9 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
Suggestion.Argument("x", "Number", false, true, Some("0")) Suggestion.Argument("x", "Number", false, true, Some("0"))
), ),
returnType = "local.Test.Main.MyType", returnType = "local.Test.Main.MyType",
scope = scope = Suggestion
Suggestion.Scope(Suggestion.Position(1, 5), Suggestion.Position(6, 0)) .Scope(Suggestion.Position(1, 5), Suggestion.Position(6, 0)),
documentation = Some("My function bar.")
) )
val local: Suggestion.Local = val local: Suggestion.Local =
@ -1911,7 +1979,8 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
scope = Suggestion.Scope( scope = Suggestion.Scope(
Suggestion.Position(3, 4), Suggestion.Position(3, 4),
Suggestion.Position(6, 0) Suggestion.Position(6, 0)
) ),
documentation = Some("Some bazz")
) )
} }
} }