Replace List with Map in Workspace

This commit is contained in:
Eric Förster 2019-03-08 13:56:53 +01:00
parent 0321795052
commit 8a955460ad
5 changed files with 59 additions and 54 deletions

View file

@ -216,7 +216,8 @@ class LatexLanguageServer : LanguageServer, LatexTextDocumentService, WorkspaceS
launch {
while (true) {
workspaceActor.withWorkspace { workspace ->
workspace.documents.map { workspace.relatedDocuments(it.uri) }
workspace.documentsByUri.values
.map { workspace.relatedDocuments(it.uri) }
.forEach { componentDatabase.await().getRelatedComponents(it) }
}
@ -273,7 +274,7 @@ class LatexLanguageServer : LanguageServer, LatexTextDocumentService, WorkspaceS
val texUri = texPath.toUri()
workspaceActor.withWorkspace { workspace ->
val document = workspace.documents.firstOrNull { it.uri == texUri }
val document = workspace.documentsByUri[texUri]
if (document != null) {
try {
val log = withContext(Dispatchers.IO) {
@ -285,7 +286,7 @@ class LatexLanguageServer : LanguageServer, LatexTextDocumentService, WorkspaceS
.groupBy { it.uri }
.mapValues { errors -> errors.value.map { it.toDiagnostic() } }
workspace.documents.forEach { publishDiagnostics(it.uri) }
workspace.documentsByUri.values.forEach { publishDiagnostics(it.uri) }
} catch (e: IOException) {
// File is still locked
}
@ -316,7 +317,7 @@ class LatexLanguageServer : LanguageServer, LatexTextDocumentService, WorkspaceS
assert(params.contentChanges.size == 1)
val uri = URIHelper.parse(params.textDocument.uri)
workspaceActor.put { workspace ->
val oldDocument = workspace.documents.first { it.uri == uri }
val oldDocument = workspace.documentsByUri.getValue(uri)
val text = params.contentChanges[0].text
val tree = when (oldDocument) {
is LatexDocument -> LatexSyntaxTree(text)
@ -433,21 +434,17 @@ class LatexLanguageServer : LanguageServer, LatexTextDocumentService, WorkspaceS
}
override fun formatting(params: DocumentFormattingParams)
: CompletableFuture<MutableList<out TextEdit>?> = future {
: CompletableFuture<List<TextEdit>?> = future {
val uri = URIHelper.parse(params.textDocument.uri)
val config = client.configuration<BibtexFormatterConfig>("bibtex.formatting", uri)
workspaceActor.withWorkspace { workspace ->
val document = workspace.documents
.filterIsInstance<BibtexDocument>()
.firstOrNull { it.uri == uri }
val document = workspace.documentsByUri[uri] as? BibtexDocument
?: return@withWorkspace null
val formatter =
BibtexFormatter(params.options.isInsertSpaces, params.options.tabSize, config.lineLength)
val edits = mutableListOf<TextEdit>()
for (entry in document.tree.root.children.filterIsInstance<BibtexDeclarationSyntax>()) {
edits.add(TextEdit(entry.range, formatter.format(entry)))
}
edits
document.tree.root.children
.filterIsInstance<BibtexDeclarationSyntax>()
.map { TextEdit(it.range, formatter.format(it)) }
}
}
@ -498,7 +495,7 @@ class LatexLanguageServer : LanguageServer, LatexTextDocumentService, WorkspaceS
private suspend fun resolveIncludes() {
workspaceActor.withWorkspace { workspace ->
for (parent in workspace.documents.filterIsInstance<LatexDocument>()) {
for (parent in workspace.documentsByUri.values.filterIsInstance<LatexDocument>()) {
for (include in parent.tree.includes) {
if (workspace.resolveDocument(parent.uri, include.path) != null) {
continue

View file

@ -11,15 +11,16 @@ import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
data class Workspace(val documents: List<Document> = listOf()) {
class Workspace(val documentsByUri: Map<URI, Document> = emptyMap()) {
fun resolveDocument(uri: URI, relativePath: String): Document? {
for (target in resolveLinkTargets(uri, relativePath)) {
val child = File(target).toURI()
val document = documents.filter { it.isFile }.firstOrNull { it.uri == child }
if (document != null) {
val document = documentsByUri[child]
if (document != null && document.isFile) {
return document
}
}
return null
}
@ -47,7 +48,8 @@ data class Workspace(val documents: List<Document> = listOf()) {
fun relatedDocuments(uri: URI): List<Document> {
val edges = mutableSetOf<Pair<Document, Document>>()
documents.filterIsInstance<LatexDocument>()
documentsByUri.values
.filterIsInstance<LatexDocument>()
.filter { it.isFile }
.forEach { parent ->
parent.tree.includes
@ -59,7 +61,7 @@ data class Workspace(val documents: List<Document> = listOf()) {
}
val results = mutableListOf<Document>()
val start = documents.firstOrNull { it.uri == uri } ?: return results
val start = documentsByUri[uri] ?: return results
val visited = mutableSetOf<Document>()
val stack = Stack<Document>()
stack.push(start)
@ -70,8 +72,8 @@ data class Workspace(val documents: List<Document> = listOf()) {
}
results.add(current)
documents.filter { edges.contains(Pair(current, it)) }
.forEach { stack.push(it) }
documentsByUri.filterValues { edges.contains(Pair(current, it)) }
.forEach { stack.push(it.value) }
}
return results
}
@ -80,7 +82,7 @@ data class Workspace(val documents: List<Document> = listOf()) {
return relatedDocuments(childUri)
.filterIsInstance<LatexDocument>()
.firstOrNull { it.tree.isStandalone }
?: documents.first { it.uri == childUri }
?: documentsByUri.getValue(childUri)
}
companion object {

View file

@ -2,6 +2,7 @@ package texlab
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.actor
import java.net.URI
import kotlin.coroutines.CoroutineContext
@ObsoleteCoroutinesApi
@ -9,17 +10,16 @@ class WorkspaceActor : CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.Default + Job()
private val actor = actor<Action> {
var documents = listOf<Document>()
val documentsByUri = mutableMapOf<URI, Document>()
for (message in channel) {
val workspace = Workspace(documents)
val workspace = Workspace(documentsByUri)
when (message) {
is Action.Get -> {
message.response.complete(workspace)
}
is Action.Put -> {
val document = message.updater(workspace)
documents = documents.filterNot { it.uri == document.uri }
.plus(document)
documentsByUri[document.uri] = document
}
}
}

View file

@ -18,14 +18,18 @@ class WorkspaceBuilder {
return URIHelper.parse(file.toURI().toString())
}
fun document(path: String, text: String): WorkspaceBuilder {
val file = File(path)
fun document(uri: URI, text: String): WorkspaceBuilder {
val file = File(uri)
val language = getLanguageByExtension(file.extension)!!
val document = Document.create(uri(path), text, language)
workspace = Workspace(workspace.documents.plus(document))
val document = Document.create(uri, text, language)
workspace = Workspace(workspace.documentsByUri.plus(Pair(document.uri, document)))
return this
}
fun document(path: String, text: String): WorkspaceBuilder {
return document(uri(path), text)
}
fun <T> request(path: String, paramsFactory: (TextDocumentIdentifier) -> T): FeatureRequest<T> {
val uri = uri(path)
val identifier = TextDocumentIdentifier(uri.toString())

View file

@ -14,51 +14,55 @@ class WorkspaceTests {
@Test
fun `it should append extensions when analyzing includes`() {
val workspace = WorkspaceBuilder()
.document("foo.tex", "\\include{bar/baz}")
val builder = WorkspaceBuilder()
val uri = builder.uri("foo.tex")
val workspace = builder
.document(uri, "\\include{bar/baz}")
.document("bar/baz.tex", "")
.workspace
val expected = workspace.documents
.map { it.uri }
.toTypedArray()
val actual = relatedDocuments(workspace, workspace.documents[0].uri)
val expected = workspace.documentsByUri.keys.toTypedArray()
val actual = relatedDocuments(workspace, uri)
assertArrayEquals(expected, actual)
}
@Test
fun `it should ignore invalid includes`() {
val workspace = WorkspaceBuilder()
.document("foo.tex", "\\include{<foo>?|bar|:}\n\\include{}")
val builder = WorkspaceBuilder()
val uri = builder.uri("foo.tex")
val workspace = builder
.document(uri, "\\include{<foo>?|bar|:}\n\\include{}")
.workspace
val expected = arrayOf(workspace.documents[0].uri)
val actual = relatedDocuments(workspace, workspace.documents[0].uri)
val expected = arrayOf(uri)
val actual = relatedDocuments(workspace, uri)
assertArrayEquals(expected, actual)
}
@Test
fun `it should find related bibliographies`() {
val workspace = WorkspaceBuilder()
.document("foo.tex", "\\addbibresource{bar.bib}")
val builder = WorkspaceBuilder()
val uri = builder.uri("foo.tex")
val workspace = builder
.document(uri, "\\addbibresource{bar.bib}")
.document("bar.bib", "")
.workspace
val expected = workspace.documents
.map { it.uri }
.toTypedArray()
val actual = relatedDocuments(workspace, workspace.documents[0].uri)
val expected = workspace.documentsByUri.keys.toTypedArray()
val actual = relatedDocuments(workspace, uri)
assertArrayEquals(expected, actual)
}
@Test
fun `it should ignore includes that cannot be resolved`() {
val workspace = WorkspaceBuilder()
.document("foo.tex", "\\include{bar.tex}")
val builder = WorkspaceBuilder()
val uri = builder.uri("foo.tex")
val workspace = builder
.document(uri, "\\include{bar.tex}")
.workspace
val expected = arrayOf(workspace.documents[0].uri)
val actual = relatedDocuments(workspace, workspace.documents[0].uri)
val expected = arrayOf(uri)
val actual = relatedDocuments(workspace, uri)
assertArrayEquals(expected, actual)
}
@ -69,10 +73,8 @@ class WorkspaceTests {
.document("bar.tex", "\\input{foo.tex}")
.workspace
val expected = workspace.documents
.map { it.uri }
.toTypedArray()
val actual = relatedDocuments(workspace, workspace.documents[0].uri)
val expected = workspace.documentsByUri.keys.toTypedArray()
val actual = relatedDocuments(workspace, workspace.documentsByUri.keys.first())
assertArrayEquals(expected, actual)
}
}