initialize.pony

use "logger"

use json = "../../json"
use rpc = "../rpc"
use rpc_data = "../rpc/data"
use c_caps = "../rpc/data/client_capabilities"
use s_caps = "../rpc/data/server_capabilities"
use "../.."
use ".."

class Initialize
  let _log: Logger[String]
  let _server: Server

  new create(
    log: Logger[String],
    server: Server)
  =>
    _log = log
    _server = server

  fun apply(
    server_state: ServerState,
    rpc_handler: rpc.Handler,
    message: rpc_data.RequestMessage,
    params: rpc_data.InitializeParams): ((ServerState | None), (I32 | None))
  =>
    _log(Fine) and _log.log(
      "request " + message.id().string() + ": " + message.method())
    _server.notify_received_request(message.id(), message.method())

    match server_state
    | ServerNotConnected =>
      _log(Error) and _log.log("initialize request before connection?")
      _server.exit()
      return (None, 1)
    | ServerNotInitialized =>
      // get position encoding
      let position_encoding =
        match _get_position_encoding(message.id(), params)
        | let pe: rpc_data.PositionEncodingKind =>
          pe
        else
          rpc_handler.respond_error(
            message.id(),
            rpc.ErrorCode.request_failed(),
            "we only handle utf-8 position encodings",
            recover val
              json.Object([ as (String, json.Item): ("retry", false) ])
            end)
          return (None, None)
        end

      // send response
      let server_info' =
        object val is rpc_data.ServerInfo
          fun val name(): String => "Eohippus Pony Language Server"
          fun val version(): String => Version()
        end
      let text_document_sync_options' =
        object val is s_caps.TextDocumentSyncOptions
          fun val openClose(): (Bool | None) => true
          fun val change(): (s_caps.TextDocumentSyncKind | None) =>
            s_caps.TextDocumentSyncIncremental
        end
      let server_capabilities' =
        object val is s_caps.ServerCapabilities
          fun val positionEncoding(): rpc_data.PositionEncodingKind =>
            position_encoding
          fun val textDocumentSync()
            : (s_caps.TextDocumentSyncOptions | None)
          =>
            text_document_sync_options'
          fun val definitionProvider()
            : (Bool | s_caps.DefinitionOptions | None)
          =>
            true
        end
      let result' =
        object val is rpc_data.InitializeResult
          fun val serverInfo(): rpc_data.ServerInfo => server_info'
          fun val capabilities(): s_caps.ServerCapabilities =>
            server_capabilities'
        end
      rpc_handler.respond(
        object val is rpc_data.ResponseMessage
          fun val id(): (I128 | String | None) => message.id()
          fun val result(): rpc_data.ResultData => result'
        end)
      _server.notify_initializing()
      _server.set_client_data(
        params.capabilities(),
        params.workspaceFolders(),
        params.rootUri(),
        params.rootPath())
      (ServerInitializing, None)
    else
      _log(Error) and _log.log("initialize request when already initialized!")
      let message_id = message.id()
      let error_code = rpc.ErrorCode.request_failed()
      let error_message = "already initialized"
      _server.notify_sent_error(message_id, error_code, error_message)
      rpc_handler.respond_error(
        message_id,
        error_code,
        error_message,
        recover val
          json.Object([ as (String, json.Item): ("retry", false) ])
        end)
      (None, None)
    end

  fun _get_position_encoding(
    msg_id: (I128 | String | None),
    params: rpc_data.InitializeParams): (rpc_data.PositionEncodingKind | None)
  =>
    // we only do utf-8
    var found_utf8 = false
    var found_utf16 = false
    match params.capabilities().general()
    | let general: c_caps.GeneralClientCapabilities =>
      match general.positionEncodings()
      | let position_encodings: Array[rpc_data.PositionEncodingKind] val =>
        for pe in position_encodings.values() do
          if pe is rpc_data.PositionEncodingUtf8 then
            found_utf8 = true
          elseif pe is rpc_data.PositionEncodingUtf16 then
            found_utf16 = true
          end
        end
      end
    end
    if found_utf8 then
      rpc_data.PositionEncodingUtf8
    elseif found_utf16 then
      rpc_data.PositionEncodingUtf16
    end