Skip to content
This repository has been archived by the owner on Jun 8, 2023. It is now read-only.

Commit

Permalink
Added document symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
dragos committed Jan 29, 2017
1 parent 66e7709 commit b0a946f
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 7 deletions.
1 change: 1 addition & 0 deletions ensime-lsp/src/main/resources/logback.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ appender("FILE", FileAppender) {

root(INFO, ["FILE"])
logger("slick", ERROR, ["FILE"])
logger("org.github.dragos.vscode", DEBUG, ["FILE"])
logger("langserver.core", INFO, ["FILE"])
logger("scala.tools.nsc", ERROR, ["FILE"])
logger("com.zaxxer.hikari", ERROR, ["FILE"])
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.github.dragos.vscode

import language.postfixOps

import java.io.File
import java.io.InputStream
import java.io.OutputStream
Expand Down Expand Up @@ -74,7 +76,8 @@ class EnsimeLanguageServer(in: InputStream, out: OutputStream) extends LanguageS
ServerCapabilities(
completionProvider = Some(CompletionOptions(false, Seq("."))),
definitionProvider = true,
hoverProvider = true
hoverProvider = true,
documentSymbolProvider = true
)
}

Expand Down Expand Up @@ -118,7 +121,11 @@ class EnsimeLanguageServer(in: InputStream, out: OutputStream) extends LanguageS

override def onOpenTextDocument(td: TextDocumentItem): Unit = {
if (ensimeActor eq null) return

val uri = new URI(td.uri)
if (uri.getScheme != "file") {
logger.info(s"Non-file URI in openTextDocument: ${td.uri}")
return
}
val f = new File(new URI(td.uri))
if (f.getAbsolutePath.startsWith(fileStore.path)) {
logger.debug(s"Not adding temporary file $f to Ensime")
Expand Down Expand Up @@ -262,6 +269,63 @@ class EnsimeLanguageServer(in: InputStream, out: OutputStream) extends LanguageS
res.map { f => Await.result(f, 5 seconds) } getOrElse Hover(Nil, None)
}

override def documentSymbols(tdi: TextDocumentIdentifier): Seq[SymbolInformation] = {
import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent.Future

if (ensimeActor ne null) {
val res: Option[Future[List[SymbolInformation]]] = for (doc <- documentManager.documentForUri(tdi.uri)) yield {

def toSymbolInformation(structure: StructureViewMember, outer: Option[String]): Seq[SymbolInformation] = {
structure match {
case StructureViewMember(keyword, name, pos, members) =>
val kind = keywordToKind.getOrElse(keyword, SymbolKind.Field)
val rest = members.flatMap(m => toSymbolInformation(m, Some(name)))
val position = pos match {
case OffsetSourcePosition(_, offset) => doc.offsetToPosition(offset)
case LineSourcePosition(_, line) => Position(line, 0)
case _ =>
logger.error(s"Unknown position for $name: $pos")
Position(0, 0)
}

SymbolInformation(
name,
kind,
Location(tdi.uri, Range(position, position.copy(character = position.character + name.length))),
outer) +: rest

case _ =>
logger.error(s"Unknown structure element: $structure")
Seq.empty
}
}

logger.info(s"Document Symbols request for ${tdi.uri}")
val future = ensimeActor ? StructureViewReq(toSourceFileInfo(tdi.uri, Some(new String(doc.contents))))

future.onComplete { f => logger.debug(s"StructureView future completed: succes? ${f.isSuccess}") }
future.map {
case StructureView(members) =>
logger.debug(s"got back: $members")
members.flatMap(m => toSymbolInformation(m, None))
}
}
res.map { f => Await.result(f, 5 seconds) } getOrElse Seq.empty
} else Seq.empty
}

private val keywordToKind = Map(
"class" -> SymbolKind.Class,
"trait" -> SymbolKind.Interface,
"type" -> SymbolKind.Interface,
"package" -> SymbolKind.Package,
"def" -> SymbolKind.Method,
"val" -> SymbolKind.Constant,
"var" -> SymbolKind.Field
)


private def toSourceFileInfo(uri: String, contents: Option[String] = None): SourceFileInfo = {
val f = new File(new URI(uri))
SourceFileInfo(RawFile(f.toPath), contents)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class LanguageServer(inStream: InputStream, outStream: OutputStream) extends Laz
gotoDefinitionRequest(textDocument, position)
case ("textDocument/hover", TextDocumentHoverRequest(TextDocumentPositionParams(textDocument, position))) =>
hoverRequest(textDocument, position)
case ("textDocument/documentSymbol", DocumentSymbolParams(tdi)) =>
DocumentSymbolResult(documentSymbols(tdi))

case (_, Shutdown()) =>
shutdown()
Expand Down Expand Up @@ -89,4 +91,8 @@ class LanguageServer(inStream: InputStream, outStream: OutputStream) extends Laz
def hoverRequest(textDocument: TextDocumentIdentifier, position: Position): Hover = {
Hover(Nil, None)
}

def documentSymbols(tdi: TextDocumentIdentifier): Seq[SymbolInformation] = {
Seq.empty
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,9 @@ case class TextDocument(uri: String, contents: Array[Char]) {
else
throw new IllegalArgumentException(s"$uri: Invalid column. Position $pos in line '${contents.slice(i, contents.size).mkString}'")
}

def lineToOffset(lineNr: Int): Int = {
positionToOffset(Position(lineNr, 0))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,16 @@ object Notification extends NotificationCompanion[Notification] {
"workspace/didChangeWatchedFiles" -> Json.format[DidChangeWatchedFiles])
}

case class DocumentSymbolResult(params: Seq[SymbolInformation]) extends ResultResponse

object ResultResponse extends ResponseCompanion[Any] {
import JsonRpcUtils._

override val ResponseFormats = Message.MessageFormats(
"initialize" -> Json.format[InitializeResult],
"textDocument/completion" -> Json.format[CompletionList],
"textDocument/definition" -> implicitly[Format[Seq[Location]]],
"textDocument/hover" -> Json.format[Hover],
"textDocument/documentSymbol" -> valueFormat(DocumentSymbolResult)(_.params),
"shutdown" -> Json.format[ShutdownResult])
}
8 changes: 3 additions & 5 deletions languageserver/src/main/scala/langserver/types/types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,9 @@ object SymbolKind {
}

case class SymbolInformation(name: String, kind: Int, location: Location, containerName: Option[String])

/**
* Parameters for a [DocumentSymbolRequest](#DocumentSymbolRequest).
*/
case class DocumentSymbolParams(textDocument: TextDocumentIdentifier)
object SymbolInformation {
implicit val format = Json.format[SymbolInformation]
}

/**
* The parameters of a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package langserver.messages



import org.scalatest.FunSuite
import langserver.types.Position

class CommandProtocolSuite extends FunSuite {
test("ServerCommand instantiastes") {
ServerCommand // the constructor may throw
}

test("ResultResponse instantiastes") {
ResultResponse // the constructor may throw
}

test("ClientCommand instantiates") {
ClientCommand // the constructor may throw
}

test("Notification instantiates") {
Notification // the constructor may throw
}
}
1 change: 1 addition & 0 deletions scala/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 0.0.6 (unreleased)

* Added document symbols (file structure)
* Automatically detect changes to .ensime and restart the server

## 0.0.5 (Jan 28, 2017)
Expand Down

0 comments on commit b0a946f

Please sign in to comment.