@@ -5,19 +5,18 @@ import com.intellij.openapi.components.Service
55import com.intellij.openapi.diagnostic.logger
66import com.intellij.openapi.editor.EditorFactory
77import com.intellij.openapi.editor.LogicalPosition
8- import com.intellij.openapi.editor.event.CaretEvent
9- import com.intellij.openapi.editor.event.CaretListener
10- import com.intellij.openapi.editor.event.EditorFactoryEvent
11- import com.intellij.openapi.editor.event.EditorFactoryListener
8+ import com.intellij.openapi.editor.event.*
9+ import com.intellij.openapi.editor.impl.DocumentImpl
1210import com.intellij.openapi.editor.markup.*
11+ import com.intellij.openapi.fileEditor.FileDocumentManager
1312import com.intellij.openapi.fileEditor.FileEditorManager
1413import com.intellij.openapi.fileEditor.FileEditorManagerListener
1514import com.intellij.openapi.fileEditor.TextEditor
1615import com.intellij.openapi.project.Project
1716import com.intellij.openapi.project.ProjectManager
1817import com.intellij.openapi.project.ProjectManagerListener
1918import com.intellij.openapi.vfs.VirtualFile
20- import com.intellij.platform.diagnostic.telemetry.EDT
19+ import com.intellij.refactoring.suggested.newRange
2120import com.intellij.ui.JBColor
2221import com.intellij.util.io.await
2322import com.intellij.util.io.awaitExit
@@ -34,8 +33,7 @@ import org.eclipse.lsp4j.jsonrpc.ResponseErrorException
3433import java.io.BufferedReader
3534import java.io.File
3635import java.io.InputStreamReader
37- import java.util.Collections
38- import java.util.LinkedList
36+ import java.util.*
3937import java.util.concurrent.Executors
4038
4139private val LOG = logger<EthersyncServiceImpl >()
@@ -50,6 +48,12 @@ class EthersyncServiceImpl(
5048 private var daemonProcess: Process ? = null
5149 private var clientProcess: Process ? = null
5250
51+ data class EthersyncRevision (
52+ var daemon : UInt = 0u ,
53+ var editor : UInt = 0u ,
54+ )
55+ val revisions: HashMap <String , EthersyncRevision > = HashMap ()
56+
5357 init {
5458 val bus = project.messageBus.connect()
5559 bus.subscribe(FileEditorManagerListener .FILE_EDITOR_MANAGER , object : FileEditorManagerListener {
@@ -71,42 +75,82 @@ class EthersyncServiceImpl(
7175 }
7276 }
7377
78+ val documentListener = object : DocumentListener {
79+ override fun documentChanged (event : DocumentEvent ) {
80+ val file = FileDocumentManager .getInstance().getFile(event.document)!!
81+ val fileEditor = FileEditorManager .getInstance(project).getEditors(file)
82+ .filterIsInstance<TextEditor >()
83+ .first()
84+
85+ val editor = fileEditor.editor
86+
87+ val uri = file.url
88+
89+ val rev = revisions.getOrPut(uri) { EthersyncRevision () };
90+ rev.editor + = 1u
91+
92+ // TODO: this calc doesn't seem right because there are some odd changes on the Neovim instance
93+ val start = editor.offsetToLogicalPosition(event.newRange.startOffset)
94+ val end = editor.offsetToLogicalPosition(event.newRange.endOffset)
95+
96+ launchEditRequest(
97+ EditRequest (
98+ uri,
99+ rev.daemon,
100+ Collections .singletonList(Delta (
101+ Range (
102+ Position (start.line, start.column),
103+ Position (end.line, end.column)
104+ ),
105+ event.newFragment.toString()
106+ ))
107+ )
108+ )
109+ }
110+ }
111+
74112 for (editor in FileEditorManager .getInstance(project).allEditors) {
75113 if (editor is TextEditor ) {
76114 editor.editor.caretModel.addCaretListener(caretListener)
115+ editor.editor.document.addDocumentListener(documentListener)
77116 }
78117 }
79118
80119 EditorFactory .getInstance().addEditorFactoryListener(object : EditorFactoryListener {
81120 override fun editorCreated (event : EditorFactoryEvent ) {
82121 event.editor.caretModel.addCaretListener(caretListener)
122+ event.editor.document.addDocumentListener(documentListener)
83123 }
84124
85125 override fun editorReleased (event : EditorFactoryEvent ) {
86126 event.editor.caretModel.removeCaretListener(caretListener)
127+ event.editor.document.removeDocumentListener(documentListener)
87128 }
88129 }, project)
89130
90131 ProjectManager .getInstance().addProjectManagerListener(project, object : ProjectManagerListener {
91132 override fun projectClosingBeforeSave (project : Project ) {
92- if (clientProcess != null ) {
93- cs.launch {
94- clientProcess!! .destroy()
95- clientProcess!! .awaitExit()
96- clientProcess = null
97- }
98- }
99- if (daemonProcess != null ) {
100- cs.launch {
101- daemonProcess!! .destroy()
102- daemonProcess!! .awaitExit()
103- daemonProcess = null
104- }
133+ cs.launch {
134+ shutdown()
105135 }
106136 }
107137 })
108138 }
109139
140+ suspend fun shutdown () {
141+ clientProcess?.let {
142+ it.destroy()
143+ it.awaitExit()
144+ clientProcess = null
145+ }
146+ daemonProcess?.let {
147+ it.destroy()
148+ it.awaitExit()
149+ daemonProcess = null
150+ }
151+ revisions.clear()
152+ }
153+
110154 override fun connectToPeer (peer : String ) {
111155 val projectDirectory = File (project.basePath!! )
112156 val ethersyncDirectory = File (projectDirectory, " .ethersync" )
@@ -118,14 +162,18 @@ class EthersyncServiceImpl(
118162 ethersyncDirectory.mkdir()
119163 }
120164
165+ val notifier = project.messageBus.syncPublisher(DaemonOutputNotifier .CHANGE_ACTION_TOPIC )
166+ if (daemonProcess != null || clientProcess != null ) {
167+ notifier.clear()
168+ shutdown()
169+ }
170+
121171 LOG .info(" Starting ethersync daemon" )
122172 val daemonProcessBuilder = ProcessBuilder (" ethersync" , " daemon" , " --peer" , peer, " --socket-name" , socket)
123173 .directory(projectDirectory)
124174 daemonProcess = daemonProcessBuilder.start()
125175 val daemonProcess = daemonProcess!!
126176
127- val notifier = project.messageBus.syncPublisher(DaemonOutputNotifier .CHANGE_ACTION_TOPIC )
128-
129177 cs.launch {
130178 val stdout = BufferedReader (InputStreamReader (daemonProcess.inputStream))
131179 stdout.use {
@@ -229,9 +277,7 @@ class EthersyncServiceImpl(
229277 }
230278
231279 private fun launchEthersyncClient (socket : String , projectDirectory : File ) {
232-
233280 cs.launch {
234-
235281 LOG .info(" Starting ethersync client" )
236282 val clientProcessBuilder = ProcessBuilder (" ethersync" , " client" , " --socket-name" , socket)
237283 .directory(projectDirectory)
@@ -302,4 +348,15 @@ class EthersyncServiceImpl(
302348 }
303349 }
304350 }
351+
352+ fun launchEditRequest (editRequest : EditRequest ) {
353+ val launcher = launcher ? : return
354+ cs.launch {
355+ try {
356+ launcher.remoteProxy.edit(editRequest).await()
357+ } catch (e: ResponseErrorException ) {
358+ TODO (" not yet implemented: notify about an protocol error" )
359+ }
360+ }
361+ }
305362}
0 commit comments