Skip to content

Commit 9a70048

Browse files
authored
Register getActiveLocation service (#74)
* Register editor service for active location * Remove extra spaces * Fix no params service request * Remove extra result param layer, make IDs ints * Add navigateToCode service method * Move service code to new class
1 parent 5c58924 commit 9a70048

File tree

6 files changed

+146
-57
lines changed

6 files changed

+146
-57
lines changed

third_party/src/main/java/com/jetbrains/lang/dart/ide/toolingDaemon/DartActiveLocationChangeHandler.kt

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package com.jetbrains.lang.dart.ide.toolingDaemon
22

3-
import com.google.gson.JsonArray
43
import com.google.gson.JsonObject
54
import com.intellij.openapi.application.readAction
6-
import com.intellij.openapi.editor.Document
5+
import com.intellij.openapi.diagnostic.logger
76
import com.intellij.openapi.editor.EditorFactory
87
import com.intellij.openapi.editor.event.*
98
import com.intellij.openapi.fileEditor.FileEditorManager
@@ -68,6 +67,7 @@ internal class DartActiveLocationChangeHandler(private val dtdService: DartTooli
6867
val activeLocationNull = paramsObject.getAsJsonObject("eventData")?.getAsJsonObject("textDocument") == null
6968
if (!activeLocationNull || !activeLocationNullSent) {
7069
activeLocationNullSent = activeLocationNull
70+
logger.info("Sending active location change event: $paramsObject")
7171
dtdService.sendRequest("postEvent", paramsObject, false) { }
7272
}
7373
}
@@ -78,52 +78,13 @@ internal class DartActiveLocationChangeHandler(private val dtdService: DartTooli
7878
paramsObject.addProperty("streamId", "Editor")
7979
paramsObject.addProperty("eventKind", "activeLocationChanged")
8080

81-
val eventDataObject = JsonObject()
82-
paramsObject.add("eventData", eventDataObject)
83-
84-
val selectionsArray = JsonArray()
85-
eventDataObject.add("selections", selectionsArray)
86-
87-
val editor = FileEditorManager.getInstance(dtdService.project).selectedTextEditor
88-
val document = editor?.document
89-
val uri = editor?.virtualFile?.takeIf { it.extension == "dart" }?.let { dtdService.getFileUri(it) }
90-
91-
if (uri != null) {
92-
val textDocumentObject = JsonObject()
93-
textDocumentObject.addProperty("uri", uri)
94-
eventDataObject.add("textDocument", textDocumentObject)
95-
96-
if (document != null) {
97-
for (caret in editor.caretModel.allCarets) {
98-
val selectionStart = caret.selectionStart
99-
val selectionEnd = caret.selectionEnd
100-
val anchorOffset = if (caret.offset == selectionStart) selectionEnd else selectionStart
101-
val activeOffset = if (caret.offset == selectionStart) selectionStart else selectionEnd
102-
val (anchorLine, anchorColumn) = getLineAndColumn(document, anchorOffset)
103-
val (activeLine, activeColumn) = getLineAndColumn(document, activeOffset)
104-
105-
val selectionObject = JsonObject()
106-
107-
selectionObject.add("anchor", JsonObject().apply {
108-
addProperty("line", anchorLine)
109-
addProperty("character", anchorColumn)
110-
})
111-
112-
selectionObject.add("active", JsonObject().apply {
113-
addProperty("line", activeLine)
114-
addProperty("character", activeColumn)
115-
})
116-
117-
selectionsArray.add(selectionObject)
118-
}
119-
}
120-
}
81+
val activeLocation = getActiveLocation(dtdService.project, dtdService)
82+
paramsObject.add("eventData", activeLocation)
12183

12284
return paramsObject
12385
}
12486

125-
private fun getLineAndColumn(document: Document, offset: Int): Pair<Int, Int> {
126-
val lineNumber = document.getLineNumber(offset)
127-
return lineNumber to offset - document.getLineStartOffset(lineNumber)
87+
companion object {
88+
private val logger = logger<DartActiveLocationChangeHandler>()
12889
}
12990
}

third_party/src/main/java/com/jetbrains/lang/dart/ide/toolingDaemon/DartToolingDaemonRequestHandler.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package com.jetbrains.lang.dart.ide.toolingDaemon
33

44
import com.google.gson.JsonObject
55

6-
class DartToolingDaemonResponse(var result: JsonObject?, var error: JsonObject?)
7-
86
fun interface DartToolingDaemonRequestHandler {
97
fun handleRequest(request: JsonObject): DartToolingDaemonResponse
108
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
package com.jetbrains.lang.dart.ide.toolingDaemon
3+
4+
import com.google.gson.JsonObject
5+
6+
class DartToolingDaemonResponse(var result: JsonObject?, var error: JsonObject?)

third_party/src/main/java/com/jetbrains/lang/dart/ide/toolingDaemon/DartToolingDaemonService.kt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,15 @@ import com.intellij.openapi.roots.ModuleRootManager
2121
import com.intellij.openapi.util.Key
2222
import com.intellij.openapi.util.io.FileUtil
2323
import com.intellij.openapi.util.text.StringUtil
24-
import com.intellij.openapi.vfs.VfsUtil
25-
import com.intellij.openapi.vfs.VfsUtilCore
26-
import com.intellij.openapi.vfs.VirtualFile
27-
import com.intellij.openapi.vfs.VirtualFileManager
24+
import com.intellij.openapi.vfs.*
2825
import com.intellij.util.EventDispatcher
2926
import com.intellij.util.PathUtil
3027
import com.intellij.util.concurrency.AppExecutorUtil
3128
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
3229
import com.intellij.util.concurrency.annotations.RequiresReadLock
3330
import com.intellij.util.io.BaseOutputReader
3431
import com.intellij.util.io.URLUtil
32+
import com.intellij.xdebugger.impl.XSourcePositionImpl
3533
import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService
3634
import com.jetbrains.lang.dart.ide.devtools.DartDevToolsService
3735
import com.jetbrains.lang.dart.sdk.DartSdk
@@ -42,7 +40,9 @@ import de.roderick.weberknecht.WebSocketEventHandler
4240
import de.roderick.weberknecht.WebSocketException
4341
import de.roderick.weberknecht.WebSocketMessage
4442
import kotlinx.coroutines.CoroutineScope
43+
import java.net.MalformedURLException
4544
import java.net.URI
45+
import java.net.URISyntaxException
4646
import java.nio.charset.StandardCharsets
4747
import java.util.concurrent.Callable
4848
import java.util.concurrent.atomic.AtomicInteger
@@ -64,7 +64,7 @@ class DartToolingDaemonService private constructor(val project: Project, cs: Cor
6464
private var lastSentRootUris: List<String> = emptyList()
6565

6666
private val nextRequestId = AtomicInteger()
67-
private val consumerMap: MutableMap<String, DartToolingDaemonConsumer> = mutableMapOf()
67+
private val consumerMap: MutableMap<Int, DartToolingDaemonConsumer> = mutableMapOf()
6868
private val servicesMap: MutableMap<String, DartToolingDaemonRequestHandler> = mutableMapOf()
6969

7070
private val eventDispatcher: EventDispatcher<DartToolingDaemonListener> = EventDispatcher.create(DartToolingDaemonListener::class.java)
@@ -109,6 +109,7 @@ class DartToolingDaemonService private constructor(val project: Project, cs: Cor
109109
connectToDtdWebSocket(it)
110110
DartAnalysisServerService.getInstance(project).connectToDtd(uri)
111111
}
112+
DtdEditorService(project, this).setUpService()
112113
}
113114

114115
private fun connectToDtdWebSocket(uri: String) {
@@ -156,7 +157,7 @@ class DartToolingDaemonService private constructor(val project: Project, cs: Cor
156157
request.addProperty("jsonrpc", "2.0")
157158
request.addProperty("method", method)
158159

159-
val id = nextRequestId.incrementAndGet().toString()
160+
val id = nextRequestId.incrementAndGet()
160161
request.addProperty("id", id)
161162
secret?.takeIf { includeSecret }?.let { params.addProperty("secret", it) }
162163
request.add("params", params)
@@ -168,7 +169,7 @@ class DartToolingDaemonService private constructor(val project: Project, cs: Cor
168169
webSocket.send(requestString)
169170
}
170171

171-
private fun sendResponse(id: String, result: JsonObject?, error: JsonObject? = null) {
172+
private fun sendResponse(id: Int, result: JsonObject?, error: JsonObject? = null) {
172173
if (!webSocketReady) {
173174
logger.warn("sendResponse(\"$id\", $result) called when the socket is not ready")
174175
return
@@ -362,15 +363,15 @@ class DartToolingDaemonService private constructor(val project: Project, cs: Cor
362363
eventDispatcher.multicaster.received(streamId, json)
363364
}
364365
else if (serviceConsumer != null) {
365-
val params = json["params"].asJsonObject
366-
val id = json["id"].asString
366+
val params = json["params"]?.asJsonObject ?: JsonObject()
367+
val id = json["id"].asInt
367368
ApplicationManager.getApplication().executeOnPooledThread {
368369
val response = serviceConsumer.handleRequest(params)
369370
sendResponse(id, response.result, response.error)
370371
}
371372
}
372373
else {
373-
val id = json["id"].asString
374+
val id = json["id"].asInt
374375
val consumer = consumerMap.remove(id)
375376
consumer?.received(json)
376377
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
package com.jetbrains.lang.dart.ide.toolingDaemon
3+
4+
import com.google.gson.JsonArray
5+
import com.google.gson.JsonObject
6+
import com.intellij.openapi.editor.Document
7+
import com.intellij.openapi.fileEditor.FileEditorManager
8+
import com.intellij.openapi.project.Project
9+
import com.intellij.util.concurrency.annotations.RequiresReadLock
10+
11+
@RequiresReadLock
12+
fun getActiveLocation(project: Project, dtdService: DartToolingDaemonService): JsonObject {
13+
val activeLocationData = JsonObject()
14+
val selectionsArray = JsonArray()
15+
activeLocationData.add("selections", selectionsArray)
16+
17+
val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: return activeLocationData
18+
val document = editor.document
19+
val virtualFile = editor.virtualFile?.takeIf { it.extension == "dart" } ?: return activeLocationData
20+
val uri = dtdService.getFileUri(virtualFile)
21+
22+
val textDocumentObject = JsonObject()
23+
textDocumentObject.addProperty("uri", uri)
24+
activeLocationData.add("textDocument", textDocumentObject)
25+
26+
for (caret in editor.caretModel.allCarets) {
27+
val selectionStart = caret.selectionStart
28+
val selectionEnd = caret.selectionEnd
29+
val anchorOffset = if (caret.offset == selectionStart) selectionEnd else selectionStart
30+
val activeOffset = caret.offset
31+
32+
val (anchorLine, anchorColumn) = getLineAndColumn(document, anchorOffset)
33+
val (activeLine, activeColumn) = getLineAndColumn(document, activeOffset)
34+
35+
val selectionObject = JsonObject()
36+
37+
selectionObject.add("anchor", JsonObject().apply {
38+
addProperty("line", anchorLine)
39+
addProperty("character", anchorColumn)
40+
})
41+
42+
selectionObject.add("active", JsonObject().apply {
43+
addProperty("line", activeLine)
44+
addProperty("character", activeColumn)
45+
})
46+
47+
selectionsArray.add(selectionObject)
48+
}
49+
50+
return activeLocationData
51+
}
52+
53+
private fun getLineAndColumn(document: Document, offset: Int): Pair<Int, Int> {
54+
val lineNumber = document.getLineNumber(offset)
55+
return lineNumber to offset - document.getLineStartOffset(lineNumber)
56+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
package com.jetbrains.lang.dart.ide.toolingDaemon
3+
4+
import com.google.gson.JsonObject
5+
import com.intellij.openapi.application.ApplicationManager
6+
import com.intellij.openapi.application.ReadAction
7+
import com.intellij.openapi.diagnostic.logger
8+
import com.intellij.openapi.project.Project
9+
import com.intellij.openapi.vfs.LocalFileSystem
10+
import com.intellij.xdebugger.impl.XSourcePositionImpl
11+
import java.net.MalformedURLException
12+
import java.net.URI
13+
import java.net.URISyntaxException
14+
15+
internal class DtdEditorService(private val project: Project, private val dtdService: DartToolingDaemonService) {
16+
fun setUpService() {
17+
dtdService.registerServiceMethod("Editor", "getActiveLocation", JsonObject()) {
18+
val result = ReadAction.nonBlocking<JsonObject> {
19+
getActiveLocation(project, dtdService)
20+
}.executeSynchronously()
21+
result.addProperty("type", "ActiveLocation")
22+
23+
DartToolingDaemonResponse(result, null)
24+
}
25+
26+
dtdService.registerServiceMethod("Editor", "navigateToCode", JsonObject()) handler@{ request ->
27+
val fileUri: String? = request.get("uri").asString
28+
if (fileUri == null) {
29+
val params = JsonObject()
30+
params.addProperty("message", "No uri provided")
31+
return@handler DartToolingDaemonResponse(null, params)
32+
}
33+
34+
var path: String? = null
35+
try {
36+
path = URI(fileUri).toURL().file
37+
} catch (e: MalformedURLException) {
38+
// A null path will cause an early return.
39+
} catch (e: URISyntaxException) {
40+
}
41+
if (path == null) {
42+
val params = JsonObject()
43+
params.addProperty("message", "Path could not be found from fileUri: $fileUri")
44+
return@handler DartToolingDaemonResponse(null, params)
45+
}
46+
47+
val file = LocalFileSystem.getInstance().findFileByPath(path)
48+
val line: Int = request.get("line").asInt
49+
val column: Int = request.get("column").asInt
50+
51+
ApplicationManager.getApplication().invokeLater(Runnable {
52+
if (file != null && line >= 0 && column >= 0) {
53+
val position = XSourcePositionImpl.create(file, line - 1, column - 1)
54+
position.createNavigatable(project).navigate(false)
55+
}
56+
})
57+
58+
val params = JsonObject()
59+
params.addProperty("success", true)
60+
DartToolingDaemonResponse(params, null)
61+
}
62+
}
63+
64+
companion object {
65+
private val logger = logger<DtdEditorService>()
66+
}
67+
}

0 commit comments

Comments
 (0)