Bagikan melalui


Protokol Server Bahasa

Apa itu Protokol Server Bahasa?

Mendukung fitur pengeditan yang kaya seperti penyelesaian otomatis kode sumber atau Buka Definisi untuk bahasa pemrograman di editor atau IDE secara tradisional sangat menantang dan memakan waktu. Biasanya perlu menulis model domain (pemindai, pengurai, pemeriksa jenis, penyusun, dan banyak lagi) dalam bahasa pemrograman editor atau IDE. Misalnya, plugin Eclipse CDT, yang menyediakan dukungan untuk C/C++ di Eclipse IDE ditulis dalam Java karena Eclipse IDE itu sendiri ditulis dalam Java. Sesuai pendekatan ini, itu artinya menerapkan model domain C/C++ di TypeScript untuk Visual Studio Code, dan model domain terpisah di C# untuk Visual Studio.

Membuat model domain khusus bahasa juga jauh lebih mudah jika alat pengembangan dapat menggunakan kembali pustaka khusus bahasa yang ada. Namun, pustaka ini biasanya diimplementasikan dalam bahasa pemrograman itu sendiri (misalnya, model domain C/C++ yang baik diimplementasikan dalam C/C++). Mengintegrasikan pustaka C/C++ ke editor yang ditulis dalam TypeScript secara teknis memungkinkan tetapi sulit dilakukan.

Server bahasa

Pendekatan lain adalah menjalankan pustaka dalam prosesnya sendiri dan menggunakan komunikasi antar-proses untuk berbicara dengannya. Pesan yang dikirim bolak-balik membentuk protokol. Protokol server bahasa (LSP) adalah produk dari standardisasi pesan yang dipertukarkan antara alat pengembangan dan proses server bahasa. Menggunakan server bahasa atau demons bukanlah ide baru atau sudah tidak asing. Editor seperti Vim dan Emacs telah melakukan ini selama beberapa waktu untuk memberikan dukungan penyelesaian otomatis semantik. Tujuan dari LSP adalah menyederhanakan integrasi semacam ini dan menyediakan kerangka kerja yang berguna untuk mengekspos fitur bahasa ke berbagai alat.

Memiliki protokol umum memungkinkan integrasi fitur bahasa pemrograman ke dalam alat pengembangan tanpa terlalu repot yaitu dengan menggunakan kembali implementasi model domain bahasa yang ada. Back-end server bahasa dapat ditulis dalam PHP, Python, atau Java dan LSP memungkinkannya diintegrasikan dengan mudah ke dalam berbagai alat. Protokol ini bekerja pada tingkat abstraksi yang umum sehingga alat dapat menawarkan layanan bahasa yang kaya tanpa perlu sepenuhnya memahami nuansa khusus untuk model domain yang mendasar.

Cara kerja pada LSP dimulai

LSP telah berevolusi dari waktu ke waktu dan saat ini sudah sampai Versi 3.0. Dimulai ketika konsep server bahasa dipilih oleh OmniSharp untuk menyediakan fitur pengeditan yang kaya untuk C#. Awalnya, OmniSharp menggunakan protokol HTTP dengan payload JSON dan telah diintegrasikan ke dalam beberapa editor termasuk Visual Studio Code.

Hampir bersamaan, Microsoft mulai bekerja pada server bahasa TypeScript, dengan ide mendukung TypeScript di editor seperti Emacs dan Sublime Text. Dalam implementasi ini, editor berkomunikasi melalui stdin/stdout dengan proses server TypeScript dan menggunakan payload JSON yang terinspirasi oleh protokol debugger V8 untuk permintaan dan respons. Server TypeScript telah diintegrasikan ke dalam plugin Sublime TypeScript dan Visual Studio Code untuk pengeditan TypeScript yang kaya.

Setelah mengintegrasikan dua server bahasa yang berbeda, tim Visual Studio Code mulai menjelajahi protokol server bahasa umum untuk editor dan IDEs. Protokol umum memungkinkan penyedia bahasa untuk membuat server bahasa tunggal yang dapat digunakan oleh IDE yang berbeda. Konsumen server bahasa hanya perlu mengimplementasikan sisi klien protokol sekali. Ini menghasilkan situasi yang sama menguntungkan untuk penyedia bahasa dan konsumen bahasa.

Protokol server bahasa dimulai dengan protokol yang digunakan oleh server TypeScript, memperluasnya dengan lebih banyak fitur bahasa yang terinspirasi oleh API bahasa Visual Studio Code. Protokol ini didukung dengan JSON-RPC untuk pemanggilan jarak jauh karena kesederhanaan dan pustaka yang ada.

Tim Visual Studio Code membuat prototipe protokol dengan menerapkan beberapa server bahasa linter yang menanggapi permintaan untuk lint (memindai) file dan mengembalikan serangkaian peringatan dan kesalahan yang terdeteksi. Tujuannya adalah untuk melakukan lint pada file saat pengguna mengedit dalam dokumen, yang artinya akan ada banyak permintaan linting selama sesi editor. Masuk akal untuk menjaga server tetap aktif dan berjalan sehingga proses linting baru tidak perlu dimulai untuk setiap pengeditan pengguna. Beberapa server linter diimplementasikan, termasuk ekstensi ESLint dan TSLint Visual Studio Code. Kedua server linter ini keduanya diimplementasikan dalam TypeScript/JavaScript dan berjalan pada Node.js. Keduanya berbagi pustaka yang mengimplementasikan bagian klien dan server dari protokol.

Cara kerja LSP

Server bahasa berjalan dalam prosesnya sendiri, dan alat seperti Visual Studio atau Visual Studio Code berkomunikasi dengan server menggunakan protokol bahasa melalui JSON-RPC. Keuntungan lain dari server bahasa yang beroperasi dalam proses khusus adalah bahwa masalah performa yang terkait dengan satu model proses dihindari. Saluran transportasi aktual dapat berupa stdio, soket, pipa yang dinamai, atau node ipc jika klien dan server ditulis dalam Node.js.

Di bawah ini adalah contoh bagaimana alat dan server bahasa berkomunikasi selama sesi pengeditan rutin:

lsp flow diagram

  • Pengguna membuka file (disebut sebagai dokumen) di alat: Alat ini memberi tahu server bahasa bahwa dokumen terbuka ('textDocument/didOpen'). Mulai sekarang, kebenaran tentang konten dokumen tidak lagi ada di sistem file tetapi disimpan oleh alat dalam memori.

  • Pengguna melakukan pengeditan: Alat ini memberi tahu server tentang perubahan dokumen ('textDocument/didChange') dan informasi semantik program diperbarui oleh server bahasa. Saat ini terjadi, server bahasa menganalisis informasi ini dan memberi tahu alat dengan kesalahan dan peringatan yang terdeteksi ('textDocument/publishDiagnostics').

  • Pengguna menjalankan "Buka Definisi" pada simbol di editor: Alat ini mengirim permintaan 'textDocument/definition' dengan dua parameter: (1) URI dokumen dan (2) posisi teks dari tempat permintaan Buka Definisi dimulai ke server. Server merespons dengan URI dokumen dan posisi definisi simbol di dalam dokumen.

  • Pengguna menutup dokumen (file): Pemberitahuan 'textDocument/didClose' dikirim dari alat, memberi tahu server bahasa bahwa dokumen sekarang tidak lagi dalam memori dan bahwa konten saat ini sekarang sudah diperbarui pada sistem file.

Contoh ini menggambarkan bagaimana protokol berkomunikasi dengan server bahasa di tingkat fitur editor seperti "Buka Definisi", "Temukan semua Referensi". Jenis data yang digunakan oleh protokol adalah editor atau IDE 'jenis data' seperti dokumen teks yang saat ini terbuka dan posisi kursor. Jenis data tidak berada pada tingkat model domain bahasa pemrograman yang biasanya akan menyediakan pohon sintaks abstrak dan simbol pengkompilasi (misalnya, jenis yang diselesaikan, namespace, ...). Ini menyederhanakan protokol secara signifikan.

Sekarang mari kita lihat permintaan 'textDocument/definition' secara lebih rinci. Di bawah ini adalah payload yang antara alat klien dan server bahasa untuk permintaan "Buka Definisi" dalam dokumen C++.

Ini adalah URL permintaan:

{
    "jsonrpc": "2.0",
    "id" : 1,
    "method": "textDocument/definition",
    "params": {
        "textDocument": {
            "uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/use.cpp"
        },
        "position": {
            "line": 3,
            "character": 12
        }
    }
}

Ini adalah URL responsnya:

{
    "jsonrpc": "2.0",
    "id": "1",
    "result": {
        "uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/provide.cpp",
        "range": {
            "start": {
                "line": 0,
                "character": 4
            },
            "end": {
                "line": 0,
                "character": 11
            }
        }
    }
}

Dalam retrospiksi, menjelaskan jenis data di tingkat editor daripada di tingkat model bahasa pemrograman adalah salah satu alasan keberhasilan protokol server bahasa. Jauh lebih mudah menstandarkan URI dokumen teks atau posisi kursor daripada menstandarkan pohon sintaks abstrak dan simbol pengkompilasi di berbagai bahasa pemrograman.

Saat pengguna bekerja dengan bahasa yang berbeda, Visual Studio Code biasanya memulai server bahasa untuk setiap bahasa pemrograman. Contoh di bawah ini menunjukkan sesi tempat pengguna bekerja pada file Java dan SASS.

java and sass

Kemampuan

Tidak setiap server bahasa dapat mendukung semua fitur yang ditentukan oleh protokol. Oleh karena itu, klien dan server mengumumkan kumpulan fitur yang didukung melalui 'kemampuan'. Sebagai contoh, server mengumumkan bahwa server dapat menangani permintaan 'textDocument/definition', tetapi mungkin tidak menangani permintaan 'workspace/symbol'. Demikian pula, klien dapat mengumumkan bahwa klien dapat memberikan pemberitahuan 'akan menyimpan' sebelum dokumen disimpan, sehingga server dapat menghitung pengeditan tekstual untuk memformat dokumen yang diedit secara otomatis.

Mengintegrasikan server bahasa

Integrasi aktual server bahasa ke dalam alat tertentu tidak ditentukan oleh protokol server bahasa dan diserahkan kepada pelaksana alat. Beberapa alat mengintegrasikan server bahasa secara umum dengan memiliki ekstensi yang dapat memulai dan berbicara dengan segala jenis server bahasa. Lainnya, seperti Visual Studio Code, membuat ekstensi kustom per server bahasa, sehingga ekstensi masih dapat menyediakan beberapa fitur bahasa kustom.

Untuk menyederhanakan implementasi server bahasa dan klien, ada pustaka atau SDK untuk klien dan bagian server. Pustaka ini disediakan untuk bahasa yang berbeda. Misalnya, ada modul npm klien bahasa untuk memudahkan integrasi server bahasa ke dalam ekstensi Visual Studio Code dan modul npm server bahasa lain untuk menulis server bahasa menggunakan Node.js. Ini adalah daftar pustaka dukungan saat ini.

Menggunakan Protokol Server Bahasa di Visual Studio