1
1
package dotty .tools .languageserver .util .server
2
2
3
3
import java .io .PrintWriter
4
+ import java .io .File .{pathSeparator , separator }
4
5
import java .net .URI
5
- import java .nio .file .Path
6
+ import java .nio .file .{ Files , Path }
6
7
import java .util
7
8
9
+ import dotty .tools .dotc .Main
10
+ import dotty .tools .dotc .reporting .{Reporter , ThrowingReporter }
11
+ import dotty .tools .io .Directory
8
12
import dotty .tools .languageserver .DottyLanguageServer
13
+ import dotty .tools .languageserver .util .Code .{TastyWithPositions , Project }
9
14
import org .eclipse .lsp4j .{ DidOpenTextDocumentParams , InitializeParams , InitializeResult , TextDocumentItem }
10
15
11
- class TestServer (testFolder : Path ) {
16
+ class TestServer (testFolder : Path , projects : List [ Project ] ) {
12
17
13
18
val server = new DottyLanguageServer
14
19
var client : TestClient = _
15
20
16
21
init()
17
22
18
23
private [this ] def init (): InitializeResult = {
19
- // Fill the configuration with values populated by sbt
20
- def showSeq [T ](lst : Seq [T ]): String =
21
- lst
22
- .map(elem => '"' + elem.toString.replace('\\ ' , '/' ) + '"' )
23
- .mkString(" [ " , " , " , " ]" )
24
- val dottyIdeJson : String =
25
- s """ [ {
26
- | "id" : "dotty-ide-test",
24
+ var compiledProjects : Set [Project ] = Set .empty
25
+
26
+ /** Compile the dependencies of the given project, and then the project. */
27
+ def compileProjectAndDependencies (project : Project ): Unit =
28
+ if (! compiledProjects.contains(project)) {
29
+ project.dependsOn.foreach(compileProjectAndDependencies)
30
+ compileProject(project)
31
+ compiledProjects += project
32
+ }
33
+
34
+ /**
35
+ * Set up given project, return JSON config.
36
+ *
37
+ * If the project has dependencies, these dependencies are compiled. The classfiles of the
38
+ * dependent projects are put on the classpath of this project.
39
+ *
40
+ * @param project The project to configure.
41
+ * @return A JSON object representing the configuration for this project.
42
+ */
43
+ def projectSetup (project : Project ): String = {
44
+ def showSeq [T ](lst : Seq [T ]): String =
45
+ lst
46
+ .map(elem => '"' + elem.toString.replace('\\ ' , '/' ) + '"' )
47
+ .mkString(" [ " , " , " , " ]" )
48
+
49
+ if (project.sources.exists(_.isInstanceOf [TastyWithPositions ])) {
50
+ compileProjectAndDependencies(project)
51
+ } else {
52
+ // Compile all the dependencies of this project
53
+ project.dependsOn.foreach(compileProjectAndDependencies)
54
+ }
55
+
56
+ s """ {
57
+ | "id" : " ${project.name}",
27
58
| "compilerVersion" : " ${BuildInfo .ideTestsCompilerVersion}",
28
59
| "compilerArguments" : ${showSeq(BuildInfo .ideTestsCompilerArguments)},
29
- | "sourceDirectories" : ${showSeq(BuildInfo .ideTestsSourceDirectories )},
30
- | "dependencyClasspath" : ${showSeq(BuildInfo .ideTestsDependencyClasspath )},
31
- | "classDirectory" : " ${BuildInfo .ideTestsClassDirectory .toString.replace('\\ ' ,'/' )}"
60
+ | "sourceDirectories" : ${showSeq(sourceDirectory(project, wipe = false ) :: Nil )},
61
+ | "dependencyClasspath" : ${showSeq(dependencyClasspath(project) )},
62
+ | "classDirectory" : " ${classDirectory(project, wipe = false ) .toString.replace('\\ ' ,'/' )}"
32
63
|}
33
- |] """ .stripMargin
64
+ | """ .stripMargin
65
+ }
66
+
67
+ Files .createDirectories(testFolder)
34
68
val configFile = testFolder.resolve(DottyLanguageServer .IDE_CONFIG_FILE )
35
- testFolder.toFile.mkdirs()
36
- testFolder.resolve(" src" ).toFile.mkdirs()
37
- testFolder.resolve(" out" ).toFile.mkdirs()
69
+ val configuration = projects.map(projectSetup).mkString(" [" , " ," , " ]" )
38
70
39
71
new PrintWriter (configFile.toString) {
40
- write(dottyIdeJson )
72
+ write(configuration )
41
73
close()
42
74
}
43
75
@@ -52,17 +84,71 @@ class TestServer(testFolder: Path) {
52
84
/** Open the code in the given file and returns the file.
53
85
* @param code code in file
54
86
* @param fileName file path in the source directory
87
+ * @param openInIDE If true, send `textDocument/didOpen` to the server.
55
88
* @return the file opened
56
89
*/
57
- def openCode (code : String , fileName : String ): TestFile = {
58
- val testFile = new TestFile (fileName)
59
- val dotdp = new DidOpenTextDocumentParams ()
90
+ def openCode (code : String , project : Project , fileName : String , openInIDE : Boolean ): TestFile = {
91
+ val testFile = new TestFile (project.name + separator + fileName)
60
92
val tdi = new TextDocumentItem ()
61
93
tdi.setUri(testFile.uri)
62
94
tdi.setText(code)
63
- dotdp.setTextDocument(tdi)
64
- server.didOpen(dotdp)
95
+
96
+ if (openInIDE) {
97
+ val dotdp = new DidOpenTextDocumentParams ()
98
+ dotdp.setTextDocument(tdi)
99
+ server.didOpen(dotdp)
100
+ }
101
+
65
102
testFile
66
103
}
67
104
105
+ private def classDirectory (project : Project , wipe : Boolean ): Path = {
106
+ val path = testFolder.resolve(project.name).resolve(" out" )
107
+ if (wipe) {
108
+ Directory (path).deleteRecursively()
109
+ Files .createDirectories(path)
110
+ }
111
+ path.toAbsolutePath
112
+ }
113
+
114
+ private def dependencyClasspath (project : Project ): Seq [String ] = {
115
+ BuildInfo .ideTestsDependencyClasspath.map(_.getAbsolutePath) ++
116
+ project.dependsOn.flatMap { dep =>
117
+ classDirectory(dep, wipe = false ).toString +: dependencyClasspath(dep)
118
+ }
119
+ }.distinct
120
+
121
+ private def sourceDirectory (project : Project , wipe : Boolean ): Path = {
122
+ val path = TestFile .sourceDir.resolve(project.name).toAbsolutePath
123
+ if (wipe) {
124
+ Directory (path).deleteRecursively()
125
+ Files .createDirectories(path)
126
+ }
127
+ path
128
+ }
129
+
130
+ /**
131
+ * Sets up the sources of the given project, creates the necessary directories
132
+ * and compile the sources.
133
+ *
134
+ * @param project The project to set up.
135
+ */
136
+ private def compileProject (project : Project ): Unit = {
137
+ val sourcesDir = sourceDirectory(project, wipe = true )
138
+ val sources = project.sources.zipWithIndex.map { case (src, id) =>
139
+ val path = sourcesDir.resolve(src.sourceName(id)).toAbsolutePath
140
+ Files .write(path, src.text.getBytes(" UTF-8" ))
141
+ path.toString
142
+ }
143
+
144
+ val compileOptions =
145
+ sources.toArray ++
146
+ Array (
147
+ " -classpath" , dependencyClasspath(project).mkString(pathSeparator),
148
+ " -d" , classDirectory(project, wipe = true ).toString
149
+ )
150
+ val reporter = new ThrowingReporter (Reporter .NoReporter )
151
+ Main .process(compileOptions, reporter)
152
+ }
153
+
68
154
}
0 commit comments