Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrating SoftParser: Go-to-definition code provider for SoftAST #360

Merged
merged 8 commits into from
Jan 30, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,23 @@ sealed trait SoftAST extends Product { self =>
final def children(): Iterator[SoftAST] =
productIterator flatMap collectASTs

final def toNode(): Node[this.type, SoftAST] =
/**
* Similar to [[org.alephium.ralph.lsp.access.compiler.ast.Tree.Source.rootNode]],
* this tree creation is also lazily evaluated and expected to have concurrent access.
*
* TODO: Move these caches to solutions like Caffeine.
*/
final lazy val toNode: Node[this.type, SoftAST] =
Node(
data = self,
children = children().map(_.toNode()).toSeq
children = children().map(_.toNode).toSeq
)

final def toCode(): String =
toNode().toCode()
toNode.toCode()

final def toStringTree(): String =
toNode().toStringTree()
toNode.toStringTree()

def toStringPretty(): String =
s"${self.getClass.getSimpleName}: ${self.index}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class AnnotationParserSpec extends AnyWordSpec with Matchers {

val annotation =
body
.toNode()
.toNode
.walkDown
.collectFirst {
case Node(annotation: SoftAST.Annotation, _) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class FnDecelerationSpec extends AnyWordSpec with Matchers {

val functions =
root
.toNode()
.toNode
.walkDown
.map(_.data)
.collect {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ object TestParser {
identifier: String,
ast: SoftAST): Option[SoftAST.Annotation] =
ast
.toNode()
.toNode
.walkDown
.collectFirst {
case Node(annotation @ SoftAST.Annotation(_, _, _, id: SoftAST.Identifier, _, _, _), _) if id.code.text == identifier =>
Expand All @@ -107,7 +107,7 @@ object TestParser {

def findFirstComment(body: SoftAST): Option[SoftAST.Comments] =
body
.toNode()
.toNode
.walkDown
.collectFirst {
case Node(comments @ SoftAST.Comments(_, _, _, _), _) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import org.alephium.ralph.lsp.pc.search.CodeProvider
import org.alephium.ralph.lsp.pc.search.completion.Suggestion
import org.alephium.ralph.lsp.pc.search.gotodef.GoToDefSetting
import org.alephium.ralph.lsp.pc.search.gotoref.GoToRefSetting
import org.alephium.ralph.lsp.pc.sourcecode.SourceLocation
import org.alephium.ralph.lsp.pc.sourcecode.{SourceCodeState, SourceLocation}
import org.alephium.ralph.lsp.pc.workspace._
import org.alephium.ralph.lsp.pc.workspace.build.error.ErrorUnknownFileType
import org.alephium.ralph.lsp.server
Expand Down Expand Up @@ -143,14 +143,14 @@ object RalphLangServer extends StrictImplicitLogging {
* @tparam O The type of [[CodeProvider]] function output.
* @return Go-to search results.
*/
def goTo[I, O <: SourceLocation.GoTo](
def goTo[S, I, O <: SourceLocation.GoTo](
fileURI: URI,
line: Int,
character: Int,
searchSettings: I,
cancelChecker: CancelChecker,
currentState: PCState
)(implicit codeProvider: CodeProvider[I, O],
)(implicit codeProvider: CodeProvider[S, I, O],
logger: ClientLogger): Iterator[O] =
if (!isFileScheme(fileURI)) {
Iterator.empty
Expand All @@ -160,7 +160,7 @@ object RalphLangServer extends StrictImplicitLogging {
currentState.workspace match {
case sourceAware: WorkspaceState.IsSourceAware =>
val goToResult =
CodeProvider.search[I, O](
CodeProvider.search[S, I, O](
line = line,
character = character,
fileURI = fileURI,
Expand Down Expand Up @@ -438,7 +438,7 @@ class RalphLangServer private (
getPCState().workspace match {
case sourceAware: WorkspaceState.IsSourceAware =>
val completionResult =
CodeProvider.search[Unit, Suggestion](
CodeProvider.search[SourceCodeState.Parsed, Unit, Suggestion](
line = line,
character = character,
fileURI = fileURI,
Expand Down Expand Up @@ -486,7 +486,7 @@ class RalphLangServer private (
val character = params.getPosition.getCharacter

val locations =
goTo[GoToDefSetting, SourceLocation.GoToDefStrict](
goTo[SourceCodeState.Parsed, GoToDefSetting, SourceLocation.GoToDefStrict](
fileURI = fileURI,
line = line,
character = character,
Expand Down Expand Up @@ -518,7 +518,7 @@ class RalphLangServer private (
)

val locations =
goTo[GoToRefSetting, SourceLocation.GoToRefStrict](
goTo[SourceCodeState.Parsed, GoToRefSetting, SourceLocation.GoToRefStrict](
fileURI = fileURI,
line = line,
character = character,
Expand All @@ -541,7 +541,7 @@ class RalphLangServer private (
val character = params.getPosition.getCharacter

val locations =
goTo[Unit, SourceLocation.GoToRenameStrict](
goTo[SourceCodeState.Parsed, Unit, SourceLocation.GoToRenameStrict](
fileURI = fileURI,
line = line,
character = character,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import java.net.URI
* @tparam I The type of search settings.
* @tparam O The type of search results.
*/
trait CodeProvider[I, O] extends Product {
trait CodeProvider[S, I, O] extends Product {

/**
* Performs a search operation at the cursor index within the source-code of a workspace.
Expand All @@ -48,7 +48,7 @@ trait CodeProvider[I, O] extends Product {
*/
def search(
cursorIndex: Int,
sourceCode: SourceCodeState.Parsed,
sourceCode: S,
workspace: WorkspaceState.IsSourceAware,
searchSettings: I
)(implicit logger: ClientLogger): Iterator[O]
Expand All @@ -58,19 +58,19 @@ trait CodeProvider[I, O] extends Product {
object CodeProvider {

/** The code-completer implementation of [[CodeProvider]]. */
implicit val codeCompleter: CodeProvider[Unit, Suggestion] =
implicit val codeCompleter: CodeProvider[SourceCodeState.Parsed, Unit, Suggestion] =
CodeCompletionProvider

/** The go-to definition implementation of [[CodeProvider]]. */
implicit val goToDefinition: CodeProvider[GoToDefSetting, SourceLocation.GoToDefStrict] =
implicit val goToDefinition: CodeProvider[SourceCodeState.Parsed, GoToDefSetting, SourceLocation.GoToDefStrict] =
GoToDefinitionProvider

/** The go-to references implementation of [[CodeProvider]]. */
implicit val goToReferences: CodeProvider[GoToRefSetting, SourceLocation.GoToRefStrict] =
implicit val goToReferences: CodeProvider[SourceCodeState.Parsed, GoToRefSetting, SourceLocation.GoToRefStrict] =
GoToReferenceProvider

/** The rename request implementation of [[CodeProvider]]. */
implicit val goToRename: CodeProvider[Unit, SourceLocation.GoToRenameStrict] =
implicit val goToRename: CodeProvider[SourceCodeState.Parsed, Unit, SourceLocation.GoToRenameStrict] =
GoToRenameProvider

/**
Expand All @@ -82,25 +82,25 @@ object CodeProvider {
* @param workspace Current workspace state.
* @tparam O The type to search.
*/
def search[I, O](
def search[S, I, O](
line: Int,
character: Int,
fileURI: URI,
workspace: WorkspaceState.IsSourceAware,
searchSettings: I
)(implicit provider: CodeProvider[I, O],
)(implicit provider: CodeProvider[S, I, O],
logger: ClientLogger): Option[Either[CompilerMessage.Error, Iterator[O]]] =
// if the fileURI belongs to the workspace, then search just within that workspace
if (URIUtil.contains(workspace.build.contractURI, fileURI))
searchWorkspace[I, O](
searchWorkspace[S, I, O](
line = line,
character = character,
fileURI = fileURI,
workspace = workspace,
searchSettings = searchSettings
)
else // else search all source files
searchWorkspaceAndDependencies[I, O](
searchWorkspaceAndDependencies[S, I, O](
line = line,
character = character,
fileURI = fileURI,
Expand All @@ -116,13 +116,13 @@ object CodeProvider {
* @param workspace Current workspace state.
* @tparam O The type to search.
*/
private def searchWorkspaceAndDependencies[I, O](
private def searchWorkspaceAndDependencies[S, I, O](
line: Int,
character: Int,
fileURI: URI,
workspace: WorkspaceState.IsSourceAware,
searchSettings: I
)(implicit provider: CodeProvider[I, O],
)(implicit provider: CodeProvider[S, I, O],
logger: ClientLogger): Option[Either[CompilerMessage.Error, Iterator[O]]] =
// Search on dependencies should only run for go-to definitions requests. Code-completion is ignored.
if (provider == CodeProvider.goToDefinition || provider == CodeProvider.goToReferences)
Expand Down Expand Up @@ -154,7 +154,7 @@ object CodeProvider {
)

// execute search on that one workspace
searchWorkspace[I, O](
searchWorkspace[S, I, O](
line = line,
character = character,
fileURI = fileURI,
Expand All @@ -174,13 +174,13 @@ object CodeProvider {
* @param workspace Current workspace state.
* @tparam O The type to search.
*/
private def searchWorkspace[I, O](
private def searchWorkspace[S, I, O](
line: Int,
character: Int,
fileURI: URI,
workspace: WorkspaceState.IsSourceAware,
searchSettings: I
)(implicit provider: CodeProvider[I, O],
)(implicit provider: CodeProvider[S, I, O],
logger: ClientLogger): Option[Either[CompilerMessage.Error, Iterator[O]]] =
WorkspaceSearcher
.findParsed( // find the parsed file where this search was executed.
Expand All @@ -202,7 +202,7 @@ object CodeProvider {
// execute the search
provider.search(
cursorIndex = cursorIndex,
sourceCode = parsed,
sourceCode = parsed.asInstanceOf[S],
workspace = workspace,
searchSettings = searchSettings
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import org.alephium.ralph.lsp.pc.workspace.build.dependency.DependencyID
*
* To execution this function invoke [[CodeProvider.search]] with [[Suggestion]] as type parameter.
*/
private[search] case object CodeCompletionProvider extends CodeProvider[Unit, Suggestion] with StrictImplicitLogging {
private[search] case object CodeCompletionProvider extends CodeProvider[SourceCodeState.Parsed, Unit, Suggestion] with StrictImplicitLogging {

/** @inheritdoc */
override def search(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import org.alephium.ralph.lsp.pc.workspace.build.dependency.DependencyID
*
* To execution this function invoke [[CodeProvider.search]] with [[SourceLocation.GoToDefStrict]] as type parameter.
*/
private[search] case object GoToDefinitionProvider extends CodeProvider[GoToDefSetting, SourceLocation.GoToDefStrict] with StrictImplicitLogging {
private[search] case object GoToDefinitionProvider extends CodeProvider[SourceCodeState.Parsed, GoToDefSetting, SourceLocation.GoToDefStrict] with StrictImplicitLogging {

/** @inheritdoc */
override def search(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import org.alephium.ralph.lsp.pc.workspace.WorkspaceState
*
* To execution this function invoke [[CodeProvider.search]] with [[Boolean]] and [[SourceLocation.GoToRefStrict]] as type parameter.
*/
private[search] case object GoToReferenceProvider extends CodeProvider[GoToRefSetting, SourceLocation.GoToRefStrict] with StrictImplicitLogging {
private[search] case object GoToReferenceProvider extends CodeProvider[SourceCodeState.Parsed, GoToRefSetting, SourceLocation.GoToRefStrict] with StrictImplicitLogging {

/** @inheritdoc */
override def search(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import org.alephium.ralph.lsp.pc.workspace.WorkspaceState
/**
* Implements [[CodeProvider]] that provides renaming results of type [[SourceLocation.GoToRenameStrict]].
*/
private[search] case object GoToRenameProvider extends CodeProvider[Unit, SourceLocation.GoToRenameStrict] {
private[search] case object GoToRenameProvider extends CodeProvider[SourceCodeState.Parsed, Unit, SourceLocation.GoToRenameStrict] {

/** @inheritdoc */
override def search(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import org.alephium.ralph.lsp.access.file.FileAccess
import org.alephium.ralph.lsp.pc.sourcecode.imports.Importer
import org.alephium.ralph.lsp.utils.log.ClientLogger
import org.alephium.ralph.lsp.utils.CollectionUtil._
import org.alephium.ralph.lsp.utils.URIUtil
import org.alephium.ralph.lsp.utils.{LazyVal, URIUtil}

import java.net.URI
import scala.annotation.tailrec
Expand Down Expand Up @@ -89,14 +89,16 @@ private[pc] object SourceCode {
SourceCodeState.ErrorParser(
fileURI = fileURI,
code = code,
errors = Seq(error)
errors = Seq(error),
astSoft = LazyVal(compiler.parseSoft(code))
)

case Right(parsedCode) =>
SourceCodeState.Parsed(
fileURI = fileURI,
code = code,
astStrict = parsedCode
astStrict = parsedCode,
astSoft = LazyVal(compiler.parseSoft(code))
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import org.alephium.ralph.{CompiledContract, CompiledScript}
import org.alephium.ralph.lsp.access.compiler.RalphParserExtension
import org.alephium.ralph.lsp.access.compiler.ast.Tree
import org.alephium.ralph.lsp.access.compiler.message.CompilerMessage
import org.alephium.ralph.lsp.access.compiler.message.error.FastParseError
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.SoftAST
import org.alephium.ralph.lsp.pc.sourcecode.warning.StringWarning
import org.alephium.ralph.lsp.utils.URIUtil
import org.alephium.ralph.lsp.utils.{LazyVal, URIUtil}

import java.net.URI

Expand Down Expand Up @@ -67,10 +69,13 @@ object SourceCodeState {

}

/** Represents: Code that is parsed and compiled. */
sealed trait IsParsedAndCompiled extends IsCodeAware

/** Represents: Code that is parsed. */
sealed trait IsParsedAndCompiled extends IsCodeAware {

def astSoft: LazyVal[Either[FastParseError, SoftAST.BlockBody]]

}

sealed trait IsParsed extends IsParsedAndCompiled

/** Represents: Code that is compiled. */
Expand All @@ -86,6 +91,9 @@ object SourceCodeState {
override def code: String =
parsed.code

override def astSoft: LazyVal[Either[FastParseError, SoftAST.BlockBody]] =
parsed.astSoft

}

/** Represents: Code that contains error(s). */
Expand Down Expand Up @@ -120,16 +128,20 @@ object SourceCodeState {
case class Parsed(
fileURI: URI,
code: String,
astStrict: Tree.Root)
astStrict: Tree.Root,
astSoft: LazyVal[Either[FastParseError, SoftAST.BlockBody]])
extends IsParsedAndCompiled
with IsParsed
Copy link
Member

@tdroxler tdroxler Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need both?

    extends IsParsedAndCompiled
       with IsParsed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right 👍🏼. We don't. Removed.


/** Represents: Error during the parser phase. */
case class ErrorParser(
fileURI: URI,
code: String,
errors: Seq[CompilerMessage.AnyError])
errors: Seq[CompilerMessage.AnyError],
astSoft: LazyVal[Either[FastParseError, SoftAST.BlockBody]])
extends IsParserOrCompilationError
with IsParsedAndCompiled
with IsParsed

/** Represents: Code is successfully compiled */
case class Compiled(
Expand Down
Loading