개요

IntelliJ와 NeoVim을 연동해서 파일 변경사항을 동기화하고, 자동완성과 린팅 등을 IntelliJ에서 받아와 NeoVim에서 사용할 수 있게 해주는 플러그인.

플러그인이 두 개가 있으며, 각각 IntelliJ와 NeoVim에 설치하여 이 둘을 통해 IntelliJ와 NeoVim이 연동되는 방식이다.

그러나 Comrade는 2020년, ComradeNeovim은 2019년 이후로는 전혀 유지보수가 되지 않는 상태.

  • ComradeNeovim
    • Java 버전으로 1.8 사용
    • IntelliJ Platform Plugin SDK 0.4.7 버전 사용
      • (2022년 3월 현재 최신 버전은 1.13.2)

문제 해결

Apple Silicon에서 작동하지 않는 문제 해결

Intel 맥에서는 문제없이 잘 작동했으나 내 M2 프로세서 Apple Silicon이 장착된 Mac Mini에서는 작동하지 않는 문제가 있었다.

원인이 되는 곳은 다음 코드였다.

NvimInstance.kt

import org.scalasbt.ipcsocket.UnixDomainSocket
                           // ↑ 여기가 문제!

private fun createRPCConnection(address: String): NeovimConnection {
    Log.info("Creating RPC connection from '$address'")

    val ipInfo = parseIPV4String(address)
    if (ipInfo!= null)
        return SocketConnection(Socket(ipInfo.first, ipInfo.second))
    else {
        val file = File(address)
        if (file.exists())
            return SocketConnection(
                    if (isWindows()) Win32NamedPipeSocketPatched(address)
                    else UnixDomainSocket(address))
                      // ↑ 여기가 문제!
    }
    throw IllegalArgumentException("Cannot create RPC connection from given address: '$address'.")
}

해결 과정을 나열해 보자면 다음과 같다.

  1. sbt의 UnixDomainSocket을 사용하면 UnsupportedOperationException("Not supported")가 던져진다.
  2. 왜 그런가? 로그 코드를 추가하고, 빌드하고, IntelliJ에 설치한 다음, IntelliJ 로그를 보면서 확인.

sun.nio.ch. SocketChannelImpl.java

@Override
public Socket socket() {
    synchronized (stateLock) {
        if (socket == null) {
            if (isNetSocket()) {
                socket = SocketAdaptor.create(this);
            } else {
                throw new UnsupportedOperationException("Not supported");
                // ↑ 여기에서 던진다
            }
        }
        return socket;
    }
}

소켓을 생성해 리턴하는 코드를 보면 isNetSocket()을 호출하는데, Comrade는 net socket이 아니라 unix domain socket을 사용하기 때문에 작동하지 않았던 것이다.

isNetSocket()isUnixSocket()

/**
 * Returns true if this channel is to a INET or INET6 socket.
 */
boolean isNetSocket() {
    return (family == INET) || (family == INET6);
}

/**
 * Returns true if this channel is to a UNIX socket.
 */
boolean isUnixSocket() {
    return (family == UNIX);
}

이걸 해결하기 위해 사흘간 다양한 시도를 해 보았고 그 과정에서 새로 알게 된 것들이 몇 가지 있었다.

삽질을 많이 하긴 했지만 결론적으로 sbt의 UnixDomainSocket을 junixsocket의 AFUNIXSocket으로 교체하여 해결할 수 있었다.

다음은 문제를 해결한 코드이다.

Merge: Fix Apple Silicon issue

createRPCConnection 함수를 좀 수정했다.

import org.newsclub.net.unix.AFUNIXSocket
import org.newsclub.net.unix.AFUNIXSocketAddress

private fun createRPCConnection(address: String): NeovimConnection {
    Log.info("Creating RPC connection from '$address'")

    val ipInfo = parseIPV4String(address)
    if (ipInfo!= null) {
        return SocketConnection(Socket(ipInfo.first, ipInfo.second))
    }

    val file = File(address)
    if (!file.exists()) {
        throw IllegalArgumentException("Cannot create RPC connection from given address: '$address'.")
    }

    if (isWindows()) {
        return SocketConnection(Win32NamedPipeSocketPatched(address))
    }

              // ↓ AFUNIXSocket을 사용해 문제 해결
    val socket : AFUNIXSocket = AFUNIXSocket.newInstance()
    socket.connect(AFUNIXSocketAddress.of(File(address)))
    return SocketConnection(socket)
}