diff --git a/build.sbt b/build.sbt index 721a08ea0c9..292aafe2b31 100644 --- a/build.sbt +++ b/build.sbt @@ -99,7 +99,8 @@ lazy val enso = (project in file(".")) `project-manager`, graph, runner, - `language-server` + `language-server`, + `text-buffer` ) .settings(Global / concurrentRestrictions += Tags.exclusive(Exclusive)) @@ -271,6 +272,17 @@ lazy val `parser-service` = (project in file("lib/parser-service")) mainClass := Some("org.enso.ParserServiceMain") ) +lazy val `text-buffer` = project + .in(file("lib/text-buffer")) + .configs(Test) + .settings( + libraryDependencies ++= Seq( + "org.typelevel" %% "cats-core" % catsVersion, + "org.scalatest" %% "scalatest" % "3.2.0-M2" % Test, + "org.scalacheck" %% "scalacheck" % "1.14.0" % Test + ) + ) + lazy val graph = (project in file("lib/graph/")) .dependsOn(logger.jvm) .configs(Test) @@ -478,6 +490,7 @@ lazy val `polyglot-api` = project ) ) .dependsOn(pkg) + .dependsOn(`text-buffer`) lazy val `language-server` = (project in file("engine/language-server")) .settings( @@ -513,6 +526,7 @@ lazy val `language-server` = (project in file("engine/language-server")) .dependsOn(`polyglot-api`) .dependsOn(`json-rpc-server`) .dependsOn(`json-rpc-server-test` % Test) + .dependsOn(`text-buffer`) lazy val runtime = (project in file("engine/runtime")) .configs(Benchmark) @@ -589,6 +603,7 @@ lazy val runtime = (project in file("engine/runtime")) .dependsOn(syntax.jvm) .dependsOn(graph) .dependsOn(`polyglot-api`) + .dependsOn(`text-buffer`) /* Note [Unmanaged Classpath] * ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/Buffer.scala b/engine/language-server/src/main/scala/org/enso/languageserver/text/Buffer.scala index d8799993c7c..02263053b9b 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/Buffer.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/text/Buffer.scala @@ -1,7 +1,7 @@ package org.enso.languageserver.text import org.enso.languageserver.data.ContentBasedVersioning -import org.enso.languageserver.data.buffer.Rope +import org.enso.text.buffer.Rope /** * A buffer state representation. diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/BufferRegistry.scala b/engine/language-server/src/main/scala/org/enso/languageserver/text/BufferRegistry.scala index 953ffc74fbe..8500c6d1bd8 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/BufferRegistry.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/text/BufferRegistry.scala @@ -22,7 +22,6 @@ import org.enso.languageserver.text.TextProtocol.{ OpenFile, SaveFile } -import org.enso.languageserver.text.editing.model.FileEdit /** * An actor that routes request regarding text editing to the right buffer. diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/CollaborativeBuffer.scala b/engine/language-server/src/main/scala/org/enso/languageserver/text/CollaborativeBuffer.scala index f68fb593f56..88388c4ffc4 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/CollaborativeBuffer.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/text/CollaborativeBuffer.scala @@ -28,8 +28,14 @@ import org.enso.languageserver.util.UnhandledLogging import org.enso.languageserver.text.Buffer.Version import org.enso.languageserver.text.CollaborativeBuffer.IOTimeout import org.enso.languageserver.text.TextProtocol._ -import org.enso.languageserver.text.editing._ -import org.enso.languageserver.text.editing.model.{FileEdit, TextEdit} +import org.enso.text.editing.{ + EditorOps, + EndPositionBeforeStartPosition, + InvalidPosition, + NegativeCoordinateInPosition, + TextEditValidationFailure +} +import org.enso.text.editing.model.TextEdit import scala.concurrent.duration._ import scala.language.postfixOps diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/FileEdit.scala b/engine/language-server/src/main/scala/org/enso/languageserver/text/FileEdit.scala new file mode 100644 index 00000000000..8c8122227aa --- /dev/null +++ b/engine/language-server/src/main/scala/org/enso/languageserver/text/FileEdit.scala @@ -0,0 +1,19 @@ +package org.enso.languageserver.text + +import org.enso.languageserver.filemanager.Path +import org.enso.text.editing.model.TextEdit + +/** + * A representation of a batch of edits to a file, versioned. + * + * @param path a path of a file + * @param edits a series of edits to a file + * @param oldVersion the current version of a buffer + * @param newVersion the version of a buffer after applying all edits + */ +case class FileEdit( + path: Path, + edits: List[TextEdit], + oldVersion: Buffer.Version, + newVersion: Buffer.Version +) diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/TextApi.scala b/engine/language-server/src/main/scala/org/enso/languageserver/text/TextApi.scala index 5faf6d18335..faaef538558 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/TextApi.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/text/TextApi.scala @@ -3,7 +3,6 @@ package org.enso.languageserver.text import org.enso.languageserver.data.CapabilityRegistration import org.enso.languageserver.filemanager.Path import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused} -import org.enso.languageserver.text.editing.model.FileEdit /** * The text editing JSON RPC API provided by the language server. diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/TextProtocol.scala b/engine/language-server/src/main/scala/org/enso/languageserver/text/TextProtocol.scala index 925f6a90c23..0c17a9f9389 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/TextProtocol.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/text/TextProtocol.scala @@ -2,7 +2,6 @@ package org.enso.languageserver.text import org.enso.languageserver.data.{CapabilityRegistration, Client} import org.enso.languageserver.filemanager.{FileSystemFailure, Path} -import org.enso.languageserver.text.editing.model.FileEdit object TextProtocol { diff --git a/engine/polyglot-api/src/main/scala/org/enso/polyglot/runtime/Runtime.scala b/engine/polyglot-api/src/main/scala/org/enso/polyglot/runtime/Runtime.scala index 00d20efc898..83c2bd56c1b 100644 --- a/engine/polyglot-api/src/main/scala/org/enso/polyglot/runtime/Runtime.scala +++ b/engine/polyglot-api/src/main/scala/org/enso/polyglot/runtime/Runtime.scala @@ -11,6 +11,7 @@ import com.fasterxml.jackson.module.scala.{ DefaultScalaModule, ScalaObjectMapper } +import org.enso.text.editing.model.TextEdit import scala.util.Try @@ -54,6 +55,22 @@ object Runtime { value = classOf[Api.PopContextResponse], name = "popContextResponse" ), + new JsonSubTypes.Type( + value = classOf[Api.OpenFileNotification], + name = "openFileNotification" + ), + new JsonSubTypes.Type( + value = classOf[Api.EditFileNotification], + name = "editFileNotification" + ), + new JsonSubTypes.Type( + value = classOf[Api.CloseFileNotification], + name = "closeFileNotification" + ), + new JsonSubTypes.Type( + value = classOf[Api.CreateFileNotification], + name = "createFileNotification" + ), new JsonSubTypes.Type( value = classOf[Api.ExpressionValuesComputed], name = "expressionValuesComputed" @@ -280,6 +297,41 @@ object Runtime { */ case class InvalidStackItemError(contextId: ContextId) extends Error + /** + * A notification sent to the server about switching a file to literal + * contents. + * + * @param path the file being moved to memory. + * @param contents the current file contents. + */ + case class OpenFileNotification(path: File, contents: String) + extends ApiRequest + + /** + * A notification sent to the server about in-memory file contents being + * edited. + * + * @param path the file being edited. + * @param edits the diffs to apply to the contents. + */ + case class EditFileNotification(path: File, edits: Seq[TextEdit]) + extends ApiRequest + + /** + * A notification sent to the server about dropping the file from memory + * back to on-disk version. + * + * @param path the file being closed. + */ + case class CloseFileNotification(path: File) extends ApiRequest + + /** + * A notification sent to the server about a file being created. + * + * @param path the newly created file. + */ + case class CreateFileNotification(path: File) extends ApiRequest + /** * Notification sent from the server to the client upon successful * initialization. Any messages sent to the server before receiving this diff --git a/engine/runtime/src/main/java/org/enso/interpreter/Language.java b/engine/runtime/src/main/java/org/enso/interpreter/Language.java index 5aefaee206e..8da757d17ae 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/Language.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/Language.java @@ -115,6 +115,6 @@ public final class Language extends TruffleLanguage { */ @Override protected Iterable findTopScopes(Context context) { - return Collections.singleton(context.compiler().topScope().getScope()); + return Collections.singleton(context.getCompiler().topScope().getScope()); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java index 4ca62b0b355..e8552b72039 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java @@ -48,7 +48,10 @@ public class ProgramRootNode extends RootNode { public Object execute(VirtualFrame frame) { if (module == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - module = new Module(QualifiedName.simpleName(sourceCode.getName()), sourceCode); + module = + new Module( + QualifiedName.simpleName(sourceCode.getName()), + sourceCode.getCharacters().toString()); } // Note [Static Passes] return module; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java index af2aeb5f677..88f53926811 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java @@ -17,7 +17,6 @@ import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.scope.LocalScope; import org.enso.interpreter.runtime.scope.ModuleScope; import org.enso.interpreter.runtime.state.Stateful; -import scala.Some; /** Node running Enso expressions passed to it as strings. */ @NodeInfo(shortName = "Eval", description = "Evaluates code passed to it as string") @@ -63,7 +62,7 @@ public abstract class EvalNode extends BaseNode { ExpressionNode expr = lookupContextReference(Language.class) .get() - .compiler() + .getCompiler() .runInline(expression, inlineContext) .getOrElse(null); if (expr == null) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Context.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Context.java index 0765be073cd..16f76160e6e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Context.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Context.java @@ -83,7 +83,7 @@ public class Context { * * @return a handle to the compiler */ - public final Compiler compiler() { + public final Compiler getCompiler() { return compiler; } @@ -126,18 +126,22 @@ public class Context { return moduleScope; } + /** + * Removes all contents from a given scope. + * + * @param scope the scope to reset. + */ public void resetScope(ModuleScope scope) { scope.reset(); initializeScope(scope); } /** - * Guess module name from the file path by comparing it with the source pathes - * of imported packages. + * Fetches the module name associated with a given file, using the environment packages + * information. * - * @param path file path. - * @return qualified module name if the function can find imported package - * with matching path. + * @param path the path to decode. + * @return a qualified name of the module corresponding to the file, if exists. */ public Optional getModuleNameForFile(File path) { return packages.stream() @@ -147,15 +151,25 @@ public class Context { } /** - * Get module from the file path. Function tries to recover module name from - * the provided file path. + * Fetches a module associated with a given file. * - * @param path file path. - * @return module if module name can be guessed from the provided file path. + * @param path the module path to lookup. + * @return the relevant module, if exists. */ public Optional getModuleForFile(File path) { return getModuleNameForFile(path) - .flatMap(n -> compiler().topScope().getModule(n.toString())); + .flatMap(n -> getCompiler().topScope().getModule(n.toString())); + } + + /** + * Registers a new module corresponding to a given file. + * + * @param path the file to register. + * @return the newly created module, if the file is a source file. + */ + public Optional createModuleForFile(File path) { + return getModuleNameForFile(path) + .map(name -> getCompiler().topScope().createModule(name, getTruffleFile(path))); } private void initializeScope(ModuleScope scope) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java index d7245c711cc..b8a265a87ce 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java @@ -25,13 +25,14 @@ import org.enso.interpreter.runtime.type.Types; import org.enso.pkg.QualifiedName; import org.enso.polyglot.LanguageInfo; import org.enso.polyglot.MethodNames; +import org.enso.text.buffer.Rope; /** Represents a source module with a known location. */ @ExportLibrary(InteropLibrary.class) public class Module implements TruffleObject { private ModuleScope scope; private TruffleFile sourceFile; - private Source literalSource; + private Rope literalSource; private boolean isParsed = false; private final QualifiedName name; @@ -46,7 +47,24 @@ public class Module implements TruffleObject { this.name = name; } - public Module(QualifiedName name, Source literalSource) { + /** + * Creates a new module. + * + * @param name the qualified name of this module. + * @param literalSource the module's source. + */ + public Module(QualifiedName name, String literalSource) { + this.literalSource = Rope.apply(literalSource); + this.name = name; + } + + /** + * Creates a new module. + * + * @param name the qualified name of this module. + * @param literalSource the module's source. + */ + public Module(QualifiedName name, Rope literalSource) { this.literalSource = literalSource; this.name = name; } @@ -62,12 +80,41 @@ public class Module implements TruffleObject { this.isParsed = true; } - public void setLiteralSource(Source source) { - this.literalSource = source; - this.sourceFile = null; + /** Clears any literal source set for this module. */ + public void unsetLiteralSource() { + this.literalSource = null; this.isParsed = false; } + /** @return the literal source of this module. */ + public Rope getLiteralSource() { + return literalSource; + } + + /** + * Sets new literal sources for the module. + * + * @param source the module source. + */ + public void setLiteralSource(String source) { + setLiteralSource(Rope.apply(source)); + } + + /** + * Sets new literal sources for the module. + * + * @param source the module source. + */ + public void setLiteralSource(Rope source) { + this.literalSource = source; + this.isParsed = false; + } + + /** + * Sets a source file for the module. + * + * @param file the module source file. + */ public void setSourceFile(TruffleFile file) { this.literalSource = null; this.sourceFile = file; @@ -95,17 +142,24 @@ public class Module implements TruffleObject { } } - public void parse(Context context) { + private void parse(Context context) { ensureScopeExists(context); context.resetScope(scope); isParsed = true; - if (sourceFile != null) { - context.compiler().run(sourceFile, scope); - } else if (literalSource != null) { - context.compiler().run(literalSource, scope); + if (literalSource != null) { + Source source = + Source.newBuilder(LanguageInfo.ID, literalSource.characters(), name.toString()).build(); + context.getCompiler().run(source, scope); + } else if (sourceFile != null) { + context.getCompiler().run(sourceFile, scope); } } + /** @return the qualified name of this module. */ + public QualifiedName getName() { + return name; + } + /** * Handles member invocations through the polyglot API. * @@ -140,7 +194,7 @@ public class Module implements TruffleObject { Source source = Source.newBuilder(LanguageInfo.ID, sourceString, scope.getAssociatedType().getName()) .build(); - context.compiler().run(source, scope); + context.getCompiler().run(source, scope); return module; } @@ -154,8 +208,7 @@ public class Module implements TruffleObject { private static Module setSource(Module module, Object[] args, Context context) throws ArityException, UnsupportedTypeException { String source = Types.extractArguments(args, String.class); - module.setLiteralSource( - Source.newBuilder(LanguageInfo.ID, source, module.name.module()).build()); + module.setLiteralSource(source); return module; } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java index 60d19b54fa7..71abefab115 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java @@ -1,6 +1,7 @@ package org.enso.interpreter.runtime.scope; import com.oracle.truffle.api.Scope; +import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.dsl.CachedContext; import com.oracle.truffle.api.dsl.Specialization; @@ -57,9 +58,25 @@ public class TopLevelScope implements TruffleObject { * @return empty result if the module does not exist or the requested module. */ public Optional getModule(String name) { + if (name.equals(Builtins.MODULE_NAME)) { + return Optional.of(builtins.getModule()); + } return Optional.ofNullable(modules.get(name)); } + /** + * Creates and registers a new module with given name and source file. + * + * @param name the module name. + * @param sourceFile the module source file. + * @return the newly created module. + */ + public Module createModule(QualifiedName name, TruffleFile sourceFile) { + Module module = new Module(name, sourceFile); + modules.put(name.toString(), module); + return module; + } + /** * Returns the builtins module. * diff --git a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java index c68d2c1555d..cac2c7ef608 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java @@ -14,9 +14,13 @@ import org.enso.interpreter.runtime.Module; import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.scope.ModuleScope; -import org.enso.pkg.QualifiedName; import java.io.File; +import org.enso.text.buffer.Rope; +import org.enso.text.editing.JavaEditorAdapter; +import org.enso.text.editing.model; + +import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -43,7 +47,7 @@ public class ExecutionService { private Optional prepareFunctionCall( String moduleName, String consName, String methodName) { - Optional moduleMay = context.compiler().topScope().getModule(moduleName); + Optional moduleMay = context.getCompiler().topScope().getModule(moduleName); if (!moduleMay.isPresent()) { return Optional.empty(); } @@ -108,13 +112,63 @@ public class ExecutionService { Consumer valueCallback, Consumer funCallCallback) throws UnsupportedMessageException, ArityException, UnsupportedTypeException { - Optional callMay = context - .getModuleNameForFile(modulePath) - .flatMap(moduleName -> prepareFunctionCall(moduleName.toString(), consName, methodName)); + Optional callMay = + context + .getModuleNameForFile(modulePath) + .flatMap( + moduleName -> prepareFunctionCall(moduleName.toString(), consName, methodName)); if (!callMay.isPresent()) { return; } execute(callMay.get(), valueCallback, funCallCallback); } + /** + * Sets a module at a given path to use a literal source. + * + * @param path the module path. + * @param contents the sources to use for it. + */ + public void setModuleSources(File path, String contents) { + Optional module = context.getModuleForFile(path); + module.ifPresent(mod -> mod.setLiteralSource(contents)); + } + + /** + * Resets a module to use on-disk sources. + * + * @param path the module path. + */ + public void resetModuleSources(File path) { + Optional module = context.getModuleForFile(path); + module.ifPresent(Module::unsetLiteralSource); + } + + /** + * Registers a new file as a source module. + * + * @param path the file to register. + */ + public void createModule(File path) { + context.createModuleForFile(path); + } + + /** + * Applies modifications to literal module sources. + * + * @param path the module to edit. + * @param edits the edits to apply. + */ + public void modifyModuleSources(File path, List edits) { + Optional moduleMay = context.getModuleForFile(path); + if (!moduleMay.isPresent()) { + return; + } + Module module = moduleMay.get(); + if (module.getLiteralSource() == null) { + return; + } + Optional editedSource = JavaEditorAdapter.applyEdits(module.getLiteralSource(), edits); + editedSource.ifPresent(module::setLiteralSource); + } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IRToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IRToTruffle.scala index 01928640408..ef473187dfa 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IRToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IRToTruffle.scala @@ -131,7 +131,7 @@ class IRToTruffle( // Register the imports in scope imports.foreach(i => - this.moduleScope.addImport(context.compiler.processImport(i.name)) + this.moduleScope.addImport(context.getCompiler.processImport(i.name)) ) // Register the atoms and their constructors in scope diff --git a/engine/runtime/src/main/scala/org/enso/interpeter/instrument/Handler.scala b/engine/runtime/src/main/scala/org/enso/interpeter/instrument/Handler.scala index 90cc2cabbc5..8b60d4b9c4c 100644 --- a/engine/runtime/src/main/scala/org/enso/interpeter/instrument/Handler.scala +++ b/engine/runtime/src/main/scala/org/enso/interpeter/instrument/Handler.scala @@ -16,6 +16,7 @@ import org.enso.polyglot.runtime.Runtime.Api import org.graalvm.polyglot.io.MessageEndpoint import scala.jdk.javaapi.OptionConverters +import scala.jdk.CollectionConverters._ /** * A message endpoint implementation used by the @@ -193,66 +194,104 @@ final class Handler { * * @param msg the message to handle. */ - def onMessage(msg: Api.Request): Unit = msg match { - case Api.Request(requestId, Api.CreateContextRequest(contextId)) => - contextManager.create(contextId) - endpoint.sendToClient( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + def onMessage(msg: Api.Request): Unit = { + val requestId = msg.requestId + msg.payload match { + case Api.CreateContextRequest(contextId) => + contextManager.create(contextId) + endpoint.sendToClient( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) - case Api.Request(requestId, Api.DestroyContextRequest(contextId)) => - if (contextManager.get(contextId).isDefined) { - contextManager.destroy(contextId) - endpoint.sendToClient( - Api.Response(requestId, Api.DestroyContextResponse(contextId)) - ) - } else { - endpoint.sendToClient( - Api.Response(requestId, Api.ContextNotExistError(contextId)) - ) + case Api.PushContextRequest(contextId, item) => { + if (contextManager.get(contextId).isDefined) { + val stack = contextManager.getStack(contextId) + val payload = item match { + case call: Api.StackItem.ExplicitCall if stack.isEmpty => + contextManager.push(contextId, item) + withContext(execute(contextId, List(call))) + Api.PushContextResponse(contextId) + case _: Api.StackItem.LocalCall if stack.nonEmpty => + contextManager.push(contextId, item) + withContext(execute(contextId, stack.toList)) + Api.PushContextResponse(contextId) + case _ => + Api.InvalidStackItemError(contextId) + } + endpoint.sendToClient(Api.Response(requestId, payload)) + } else { + endpoint.sendToClient( + Api.Response(requestId, Api.ContextNotExistError(contextId)) + ) + } } - case Api.Request(requestId, Api.PushContextRequest(contextId, item)) => { - if (contextManager.get(contextId).isDefined) { - val stack = contextManager.getStack(contextId) - val payload = item match { - case call: Api.StackItem.ExplicitCall if stack.isEmpty => - contextManager.push(contextId, item) - withContext(execute(contextId, List(call))) - Api.PushContextResponse(contextId) - case _: Api.StackItem.LocalCall if stack.nonEmpty => - contextManager.push(contextId, item) - withContext(execute(contextId, stack.toList)) - Api.PushContextResponse(contextId) - case _ => - Api.InvalidStackItemError(contextId) + case Api.PopContextRequest(contextId) => + if (contextManager.get(contextId).isDefined) { + val payload = contextManager.pop(contextId) match { + case Some(_: Api.StackItem.ExplicitCall) => + Api.PopContextResponse(contextId) + case Some(_: Api.StackItem.LocalCall) => + withContext( + execute(contextId, contextManager.getStack(contextId).toList) + ) + Api.PopContextResponse(contextId) + case None => + Api.EmptyStackError(contextId) + } + endpoint.sendToClient(Api.Response(requestId, payload)) + } else { + endpoint.sendToClient( + Api.Response(requestId, Api.ContextNotExistError(contextId)) + ) + } + + case Api.DestroyContextRequest(contextId) => + if (contextManager.get(contextId).isDefined) { + contextManager.destroy(contextId) + endpoint.sendToClient( + Api.Response(requestId, Api.DestroyContextResponse(contextId)) + ) + } else { + endpoint.sendToClient( + Api.Response(requestId, Api.ContextNotExistError(contextId)) + ) + } + + case Api.PushContextRequest(contextId, item) => + val payload = contextManager.push(contextId, item) match { + case Some(()) => Api.PushContextResponse(contextId) + case None => Api.ContextNotExistError(contextId) } endpoint.sendToClient(Api.Response(requestId, payload)) - } else { - endpoint.sendToClient( - Api.Response(requestId, Api.ContextNotExistError(contextId)) - ) - } + + case Api.PopContextRequest(contextId) => + if (contextManager.get(contextId).isDefined) { + val payload = contextManager.pop(contextId) match { + case Some(_) => Api.PopContextResponse(contextId) + case None => Api.EmptyStackError(contextId) + } + endpoint.sendToClient(Api.Response(requestId, payload)) + } else { + endpoint.sendToClient( + Api.Response(requestId, Api.ContextNotExistError(contextId)) + ) + } + + case Api.OpenFileNotification(path, contents) => + executionService.setModuleSources(path, contents) + + case Api.CreateFileNotification(path) => + executionService.createModule(path) + + case Api.OpenFileNotification(path, contents) => + executionService.setModuleSources(path, contents) + + case Api.CloseFileNotification(path) => + executionService.resetModuleSources(path) + + case Api.EditFileNotification(path, edits) => + executionService.modifyModuleSources(path, edits.asJava) } - - case Api.Request(requestId, Api.PopContextRequest(contextId)) => - if (contextManager.get(contextId).isDefined) { - val payload = contextManager.pop(contextId) match { - case Some(_: Api.StackItem.ExplicitCall) => - Api.PopContextResponse(contextId) - case Some(_: Api.StackItem.LocalCall) => - withContext( - execute(contextId, contextManager.getStack(contextId).toList) - ) - Api.PopContextResponse(contextId) - case None => - Api.EmptyStackError(contextId) - } - endpoint.sendToClient(Api.Response(requestId, payload)) - } else { - endpoint.sendToClient( - Api.Response(requestId, Api.ContextNotExistError(contextId)) - ) - } } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala index 697f55affbc..cb369ae97d2 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala @@ -1,19 +1,21 @@ package org.enso.interpreter.test.instrument -import java.io.File +import java.io.{ByteArrayOutputStream, File, OutputStream} import java.nio.ByteBuffer import java.nio.file.Files import java.util.UUID import org.enso.interpreter.test.Metadata import org.enso.pkg.Package -import org.enso.polyglot.runtime.Runtime.Api +import org.enso.polyglot.runtime.Runtime.{Api, ApiRequest} import org.enso.polyglot.{ LanguageInfo, PolyglotContext, RuntimeOptions, RuntimeServerInfo } +import org.enso.text.editing.model +import org.enso.text.editing.model.TextEdit import org.graalvm.polyglot.Context import org.graalvm.polyglot.io.MessageEndpoint import org.scalatest.BeforeAndAfterEach @@ -30,8 +32,11 @@ class RuntimeServerTest class TestContext(packageName: String) { var endPoint: MessageEndpoint = _ var messageQueue: List[Api.Response] = List() - val tmpDir: File = Files.createTempDirectory("enso-test-packages").toFile - val pkg: Package = Package.create(tmpDir, packageName) + + val tmpDir: File = Files.createTempDirectory("enso-test-packages").toFile + + val pkg: Package = Package.create(tmpDir, packageName) + val out: ByteArrayOutputStream = new ByteArrayOutputStream() val executionContext = new PolyglotContext( Context .newBuilder(LanguageInfo.ID) @@ -39,6 +44,7 @@ class RuntimeServerTest .allowAllAccess(true) .option(RuntimeOptions.getPackagesPathOption, pkg.root.getAbsolutePath) .option(RuntimeServerInfo.ENABLE_OPTION, "true") + .out(out) .serverTransport { (uri, peer) => if (uri.toString == RuntimeServerInfo.URI) { endPoint = peer @@ -60,8 +66,11 @@ class RuntimeServerTest ) executionContext.context.initialize(LanguageInfo.ID) - def writeMain(contents: String): File = { + def writeMain(contents: String): File = Files.write(pkg.mainFile.toPath, contents.getBytes).toFile + + def writeFile(file: File, contents: String): Unit = { + Files.write(file.toPath, contents.getBytes): Unit } def send(msg: Api.Request): Unit = endPoint.sendBinary(Api.serialize(msg)) @@ -71,6 +80,12 @@ class RuntimeServerTest messageQueue = messageQueue.drop(1) msg } + + def consumeOut: List[String] = { + val result = out.toString + out.reset() + result.linesIterator.toList + } } object Program { @@ -276,4 +291,68 @@ class RuntimeServerTest ) } + "Runtime server" should "support file modification operations" in { + def send(msg: ApiRequest): Unit = + context.send(Api.Request(UUID.randomUUID(), msg)) + + val fooFile = new File(context.pkg.sourceDir, "Foo.enso") + val contextId = UUID.randomUUID() + + send(Api.CreateContextRequest(contextId)) + context.receive + + def push: Unit = + send( + Api.PushContextRequest( + contextId, + Api.StackItem + .ExplicitCall( + Api.MethodPointer(fooFile, "Foo", "main"), + None, + Vector() + ) + ) + ) + def pop: Unit = send(Api.PopContextRequest(contextId)) + + // Create a new file + context.writeFile(fooFile, "main = IO.println \"I'm a file!\"") + send(Api.CreateFileNotification(fooFile)) + push + context.consumeOut shouldEqual List("I'm a file!") + + // Open the new file and set literal source + send( + Api.OpenFileNotification( + fooFile, + "main = IO.println \"I'm an open file!\"" + ) + ) + pop + push + context.consumeOut shouldEqual List("I'm an open file!") + + // Modify the file + send( + Api.EditFileNotification( + fooFile, + Seq( + TextEdit( + model.Range(model.Position(0, 24), model.Position(0, 30)), + " modified" + ) + ) + ) + ) + pop + push + context.consumeOut shouldEqual List("I'm a modified file!") + + // Close the file + send(Api.CloseFileNotification(fooFile)) + pop + push + context.consumeOut shouldEqual List("I'm a file!") + + } } diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/CharView.scala b/lib/text-buffer/src/main/scala/org/enso/text/buffer/CharView.scala similarity index 97% rename from engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/CharView.scala rename to lib/text-buffer/src/main/scala/org/enso/text/buffer/CharView.scala index 63a1e40dc85..b5166488402 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/CharView.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/buffer/CharView.scala @@ -1,4 +1,4 @@ -package org.enso.languageserver.data.buffer +package org.enso.text.buffer /** * Exposes a character-based API for rope operations. @@ -72,6 +72,8 @@ case class CharView(rope: Rope) extends CharSequence { start: Int, end: Int ): CharSequence = CharView(substring(start, end)) + + override def toString: String = rope.toString } object CharView { diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/CodePointView.scala b/lib/text-buffer/src/main/scala/org/enso/text/buffer/CodePointView.scala similarity index 98% rename from engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/CodePointView.scala rename to lib/text-buffer/src/main/scala/org/enso/text/buffer/CodePointView.scala index b4474131cba..37caf8a6316 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/CodePointView.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/buffer/CodePointView.scala @@ -1,7 +1,8 @@ -package org.enso.languageserver.data.buffer +package org.enso.text.buffer /** * Exposes a code points based view over rope indexing operations. + * * @param rope the underlying rope. */ case class CodePointView(rope: Rope) { diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/LineView.scala b/lib/text-buffer/src/main/scala/org/enso/text/buffer/LineView.scala similarity index 97% rename from engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/LineView.scala rename to lib/text-buffer/src/main/scala/org/enso/text/buffer/LineView.scala index a8913467499..cd2bc2d3202 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/LineView.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/buffer/LineView.scala @@ -1,7 +1,8 @@ -package org.enso.languageserver.data.buffer +package org.enso.text.buffer /** * Exposes a line-based API for the rope. + * * @param rope the underlying rope. */ case class LineView(rope: Rope) { diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/Rope.scala b/lib/text-buffer/src/main/scala/org/enso/text/buffer/Rope.scala similarity index 96% rename from engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/Rope.scala rename to lib/text-buffer/src/main/scala/org/enso/text/buffer/Rope.scala index e9b12e3b5bd..53d04a70f94 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/Rope.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/buffer/Rope.scala @@ -1,8 +1,10 @@ -package org.enso.languageserver.data.buffer -import cats.kernel.Monoid +package org.enso.text.buffer + +import cats.Monoid /** * The measure used for storing strings in the b-tree. + * * @param utf16Size number of characters. * @param utf32Size number of code points. * @param fullLines number of lines terminated with a new line character. @@ -66,7 +68,7 @@ case class Rope(root: Node[String, StringMeasure]) { */ override def toString: String = { val sb = new StringBuilder(root.measure.utf16Size) - root.value.foreach { str => val _ = sb.append(str) } + root.value.foreach { str => sb.append(str) } sb.toString() } diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/StringUtils.scala b/lib/text-buffer/src/main/scala/org/enso/text/buffer/StringUtils.scala similarity index 97% rename from engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/StringUtils.scala rename to lib/text-buffer/src/main/scala/org/enso/text/buffer/StringUtils.scala index ce3495a95ae..c442e58ba89 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/StringUtils.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/buffer/StringUtils.scala @@ -1,4 +1,5 @@ -package org.enso.languageserver.data.buffer +package org.enso.text.buffer + import scala.collection.mutable.ArrayBuffer object StringUtils { diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/Tree.scala b/lib/text-buffer/src/main/scala/org/enso/text/buffer/Tree.scala similarity index 99% rename from engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/Tree.scala rename to lib/text-buffer/src/main/scala/org/enso/text/buffer/Tree.scala index 804cb68605f..3b95ad40239 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/Tree.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/buffer/Tree.scala @@ -1,6 +1,6 @@ -package org.enso.languageserver.data.buffer +package org.enso.text.buffer -import cats.kernel.Monoid +import cats.Monoid /** * A super class of nodes stored in the tree. diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/TreeOps.scala b/lib/text-buffer/src/main/scala/org/enso/text/buffer/TreeOps.scala similarity index 98% rename from engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/TreeOps.scala rename to lib/text-buffer/src/main/scala/org/enso/text/buffer/TreeOps.scala index 7716d32ddaa..266d60af281 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/data/buffer/TreeOps.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/buffer/TreeOps.scala @@ -1,4 +1,4 @@ -package org.enso.languageserver.data.buffer +package org.enso.text.buffer /** * Encodes element-level operation on a tree. diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/EditorOps.scala b/lib/text-buffer/src/main/scala/org/enso/text/editing/EditorOps.scala similarity index 81% rename from engine/language-server/src/main/scala/org/enso/languageserver/text/editing/EditorOps.scala rename to lib/text-buffer/src/main/scala/org/enso/text/editing/EditorOps.scala index da7be81e039..7a0f12d6984 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/EditorOps.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/editing/EditorOps.scala @@ -1,8 +1,7 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing import cats.implicits._ -import org.enso.languageserver.text.editing.TextEditValidator.validate -import org.enso.languageserver.text.editing.model.TextEdit +import org.enso.text.editing.model.TextEdit /** * Auxiliary operations that edit a buffer validating diffs before applying it. @@ -25,7 +24,9 @@ object EditorOps { * @return either validation failure or a modified buffer */ def edit[A: TextEditor](buffer: A, diff: TextEdit): EditorOp[A] = - validate(buffer, diff).map(_ => TextEditor[A].edit(buffer, diff)) + TextEditValidator + .validate(buffer, diff) + .map(_ => TextEditor[A].edit(buffer, diff)) /** * Applies a series of edits to the buffer and validates each diff against diff --git a/lib/text-buffer/src/main/scala/org/enso/text/editing/JavaEditorAdapter.scala b/lib/text-buffer/src/main/scala/org/enso/text/editing/JavaEditorAdapter.scala new file mode 100644 index 00000000000..7d445e85a51 --- /dev/null +++ b/lib/text-buffer/src/main/scala/org/enso/text/editing/JavaEditorAdapter.scala @@ -0,0 +1,31 @@ +package org.enso.text.editing + +import java.util.Optional + +import org.enso.text.buffer.Rope +import org.enso.text.editing.model.TextEdit + +import scala.jdk.CollectionConverters._ + +/** + * A convenience class for using the text editor logic from Java code. + */ +object JavaEditorAdapter { + implicit private val editor: TextEditor[Rope] = RopeTextEditor + + /** + * Applies a series of edits to a given text. + * + * @param rope the initial text. + * @param edits the edits to apply. + * @return the result of applying edits, if they pass the validations. + */ + def applyEdits( + rope: Rope, + edits: java.util.List[TextEdit] + ): java.util.Optional[Rope] = + EditorOps + .applyEdits(rope, edits.asScala.toList) + .map(Optional.of[Rope]) + .getOrElse(Optional.empty[Rope]()) +} diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/RopeTextEditor.scala b/lib/text-buffer/src/main/scala/org/enso/text/editing/RopeTextEditor.scala similarity index 91% rename from engine/language-server/src/main/scala/org/enso/languageserver/text/editing/RopeTextEditor.scala rename to lib/text-buffer/src/main/scala/org/enso/text/editing/RopeTextEditor.scala index 42331ea1a1d..248289b1169 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/RopeTextEditor.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/editing/RopeTextEditor.scala @@ -1,7 +1,7 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing -import org.enso.languageserver.data.buffer.Rope -import org.enso.languageserver.text.editing.model.TextEdit +import org.enso.text.buffer.Rope +import org.enso.text.editing.model.TextEdit /** * Instance of the [[TextEditor]] type class for the [[Rope]] type. diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditValidationFailure.scala b/lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditValidationFailure.scala similarity index 84% rename from engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditValidationFailure.scala rename to lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditValidationFailure.scala index d6ea23ba52d..35b4a1bbb4d 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditValidationFailure.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditValidationFailure.scala @@ -1,6 +1,6 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing -import org.enso.languageserver.text.editing.model.Position +import org.enso.text.editing.model.Position /** * Base trait for text edit validation failures. diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditValidator.scala b/lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditValidator.scala similarity index 94% rename from engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditValidator.scala rename to lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditValidator.scala index f011ffacf47..7847a03f029 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditValidator.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditValidator.scala @@ -1,7 +1,7 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing -import org.enso.languageserver.text.editing.model.TextEdit import cats.implicits._ +import org.enso.text.editing.model.TextEdit /** * A validator of [[TextEdit]] object. diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditor.scala b/lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditor.scala similarity index 86% rename from engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditor.scala rename to lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditor.scala index 757f8025a2d..d00dcd9df36 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/TextEditor.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/editing/TextEditor.scala @@ -1,7 +1,7 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing -import org.enso.languageserver.data.buffer.Rope -import org.enso.languageserver.text.editing.model.TextEdit +import org.enso.text.buffer.Rope +import org.enso.text.editing.model.TextEdit /** * TextEditor is a type class that specifies a set of function for text diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/model.scala b/lib/text-buffer/src/main/scala/org/enso/text/editing/model.scala similarity index 64% rename from engine/language-server/src/main/scala/org/enso/languageserver/text/editing/model.scala rename to lib/text-buffer/src/main/scala/org/enso/text/editing/model.scala index 5cd14f89e2c..1602731f4aa 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/text/editing/model.scala +++ b/lib/text-buffer/src/main/scala/org/enso/text/editing/model.scala @@ -1,7 +1,4 @@ -package org.enso.languageserver.text.editing - -import org.enso.languageserver.filemanager.Path -import org.enso.languageserver.text.Buffer +package org.enso.text.editing object model { @@ -44,19 +41,4 @@ object model { */ case class TextEdit(range: Range, text: String) - /** - * A representation of a batch of edits to a file, versioned. - * - * @param path a path of a file - * @param edits a series of edits to a file - * @param oldVersion the current version of a buffer - * @param newVersion the version of a buffer after applying all edits - */ - case class FileEdit( - path: Path, - edits: List[TextEdit], - oldVersion: Buffer.Version, - newVersion: Buffer.Version - ) - } diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/text/Generators.scala b/lib/text-buffer/src/test/scala/org/enso/text/Generators.scala similarity index 95% rename from engine/language-server/src/test/scala/org/enso/languageserver/text/Generators.scala rename to lib/text-buffer/src/test/scala/org/enso/text/Generators.scala index 14fc2cb362a..d921b77ad10 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/text/Generators.scala +++ b/lib/text-buffer/src/test/scala/org/enso/text/Generators.scala @@ -1,4 +1,5 @@ -package org.enso.languageserver.text +package org.enso.text + import org.scalacheck.Arbitrary.arbitrary import org.scalacheck.Gen diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/text/MockBuffer.scala b/lib/text-buffer/src/test/scala/org/enso/text/MockBuffer.scala similarity index 78% rename from engine/language-server/src/test/scala/org/enso/languageserver/text/MockBuffer.scala rename to lib/text-buffer/src/test/scala/org/enso/text/MockBuffer.scala index ea9fb55b222..04483919810 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/text/MockBuffer.scala +++ b/lib/text-buffer/src/test/scala/org/enso/text/MockBuffer.scala @@ -1,5 +1,6 @@ -package org.enso.languageserver.text -import org.enso.languageserver.data.buffer.StringUtils +package org.enso.text + +import org.enso.text.buffer.StringUtils case class MockBuffer(lines: List[String]) { override def toString: String = lines.mkString("") diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/text/RopeSpecification.scala b/lib/text-buffer/src/test/scala/org/enso/text/RopeSpecification.scala similarity index 98% rename from engine/language-server/src/test/scala/org/enso/languageserver/text/RopeSpecification.scala rename to lib/text-buffer/src/test/scala/org/enso/text/RopeSpecification.scala index 2276b43dd0c..6300681a2fe 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/text/RopeSpecification.scala +++ b/lib/text-buffer/src/test/scala/org/enso/text/RopeSpecification.scala @@ -1,5 +1,6 @@ -package org.enso.languageserver.text -import org.enso.languageserver.data.buffer.Rope +package org.enso.text + +import org.enso.text.buffer.Rope import org.scalacheck.Prop.forAll import org.scalacheck.Arbitrary._ import org.scalacheck.Properties diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/text/StringUtilsSpecification.scala b/lib/text-buffer/src/test/scala/org/enso/text/StringUtilsSpecification.scala similarity index 90% rename from engine/language-server/src/test/scala/org/enso/languageserver/text/StringUtilsSpecification.scala rename to lib/text-buffer/src/test/scala/org/enso/text/StringUtilsSpecification.scala index e376751d74d..745ce3a3696 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/text/StringUtilsSpecification.scala +++ b/lib/text-buffer/src/test/scala/org/enso/text/StringUtilsSpecification.scala @@ -1,5 +1,6 @@ -package org.enso.languageserver.text -import org.enso.languageserver.data.buffer.StringUtils +package org.enso.text + +import org.enso.text.buffer.StringUtils import org.scalacheck.Properties import org.scalacheck.Prop.forAll diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/text/editing/EditorOpsSpec.scala b/lib/text-buffer/src/test/scala/org/enso/text/editing/EditorOpsSpec.scala similarity index 89% rename from engine/language-server/src/test/scala/org/enso/languageserver/text/editing/EditorOpsSpec.scala rename to lib/text-buffer/src/test/scala/org/enso/text/editing/EditorOpsSpec.scala index 2a718b4cdce..b5ecae033d0 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/text/editing/EditorOpsSpec.scala +++ b/lib/text-buffer/src/test/scala/org/enso/text/editing/EditorOpsSpec.scala @@ -1,7 +1,7 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing -import org.enso.languageserver.text.editing.TestData.testSnippet -import org.enso.languageserver.text.editing.model.{Position, Range, TextEdit} +import TestData.testSnippet +import org.enso.text.editing.model.{Position, Range, TextEdit} import org.scalatest.EitherValues import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.must.Matchers diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/text/editing/RopeTextEditorSpec.scala b/lib/text-buffer/src/test/scala/org/enso/text/editing/RopeTextEditorSpec.scala similarity index 93% rename from engine/language-server/src/test/scala/org/enso/languageserver/text/editing/RopeTextEditorSpec.scala rename to lib/text-buffer/src/test/scala/org/enso/text/editing/RopeTextEditorSpec.scala index 4d897a50241..fb454c10b94 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/text/editing/RopeTextEditorSpec.scala +++ b/lib/text-buffer/src/test/scala/org/enso/text/editing/RopeTextEditorSpec.scala @@ -1,8 +1,8 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing -import org.enso.languageserver.data.buffer.Rope -import org.enso.languageserver.text.editing.TestData._ -import org.enso.languageserver.text.editing.model.{Position, Range, TextEdit} +import TestData._ +import org.enso.text.buffer.Rope +import org.enso.text.editing.model.{Position, Range, TextEdit} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.must.Matchers diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/text/editing/TestData.scala b/lib/text-buffer/src/test/scala/org/enso/text/editing/TestData.scala similarity index 73% rename from engine/language-server/src/test/scala/org/enso/languageserver/text/editing/TestData.scala rename to lib/text-buffer/src/test/scala/org/enso/text/editing/TestData.scala index 5db01045b24..75a76e0d796 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/text/editing/TestData.scala +++ b/lib/text-buffer/src/test/scala/org/enso/text/editing/TestData.scala @@ -1,6 +1,6 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing -import org.enso.languageserver.data.buffer.Rope +import org.enso.text.buffer.Rope object TestData { diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/text/editing/TextEditValidatorSpec.scala b/lib/text-buffer/src/test/scala/org/enso/text/editing/TextEditValidatorSpec.scala similarity index 87% rename from engine/language-server/src/test/scala/org/enso/languageserver/text/editing/TextEditValidatorSpec.scala rename to lib/text-buffer/src/test/scala/org/enso/text/editing/TextEditValidatorSpec.scala index c0da97b7583..84fa76c82ce 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/text/editing/TextEditValidatorSpec.scala +++ b/lib/text-buffer/src/test/scala/org/enso/text/editing/TextEditValidatorSpec.scala @@ -1,10 +1,10 @@ -package org.enso.languageserver.text.editing +package org.enso.text.editing -import org.enso.languageserver.data.buffer.Rope -import org.enso.languageserver.text.editing.TextEditValidator.validate -import org.enso.languageserver.text.editing.model._ +import org.enso.text.buffer.Rope +import org.enso.text.editing.model.{Position, Range, TextEdit} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.must.Matchers +import TextEditValidator.validate class TextEditValidatorSpec extends AnyFlatSpec with Matchers {