Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Vad är Language Server Protocol?
Stöd för omfattande redigeringsfunktioner som automatisk komplettering av källkod eller Gå till definition för ett programmeringsspråk i en redigerare eller IDE är traditionellt mycket utmanande och tidskrävande. Vanligtvis krävs det att du skriver en domänmodell (en skanner, en parser, en typkontroll, en byggare med mera) i programmeringsspråket för redigeraren eller IDE. Till exempel är Eclipse CDT-plugin-programmet, som ger stöd för C/C++ i Eclipse IDE, skrivs i Java eftersom Eclipse IDE är skrivet i Java. Med den här metoden skulle det innebära att implementera en C/C++-domänmodell i TypeScript för Visual Studio Code och en separat domänmodell i C# för Visual Studio.
Det är också mycket enklare att skapa språkspecifika domänmodeller om ett utvecklingsverktyg kan återanvända befintliga språkspecifika bibliotek. Dessa bibliotek implementeras dock vanligtvis i själva programmeringsspråket (till exempel implementeras bra C/C++-domänmodeller i C/C++). Att integrera ett C/C++-bibliotek i en redigerare som skrivits i TypeScript är tekniskt möjligt men svårt att göra.
Språkservrar
En annan metod är att köra biblioteket i en egen process och använda kommunikation mellan processer för att prata med det. De meddelanden som skickas fram och tillbaka utgör ett protokoll. Språkserverprotokollet (LSP) är en produkt av standardisering av meddelanden som utbyts mellan ett utvecklingsverktyg och en språkserverprocess. Att använda språkservrar eller demoner är inte en ny eller ny idé. Redigerare som Vim och Emacs har gjort detta under en tid för att tillhandahålla semantisk automatisk kompletteringsstöd. Målet med LSP var att förenkla den här typen av integreringar och tillhandahålla ett användbart ramverk för att exponera språkfunktioner för en mängd olika verktyg.
Med ett gemensamt protokoll kan du integrera programmeringsspråkfunktioner i ett utvecklingsverktyg med minimalt krångel genom att återanvända en befintlig implementering av språkets domänmodell. En serverdel för språk kan skrivas i PHP, Python eller Java och med LSP kan den enkelt integreras i en mängd olika verktyg. Protokollet fungerar på en gemensam abstraktionsnivå så att ett verktyg kan erbjuda omfattande språktjänster utan att helt behöva förstå de nyanser som är specifika för den underliggande domänmodellen.
Hur arbetet med LSP började
LSP har utvecklats med tiden och idag är den version 3.0. Det började med att konceptet med en språkserver hämtades av OmniSharp för att tillhandahålla omfattande redigeringsfunktioner för C#. Inledningsvis använde OmniSharp HTTP-protokollet med en JSON-nyttolast och har integrerats i flera redigerare, inklusive Visual Studio Code.
Ungefär samtidigt började Microsoft arbeta på en TypeScript-språkserver, med idén att stödja TypeScript i redigeringsprogram som Emacs och Sublime Text. I den här implementeringen kommunicerar en redigerare via stdin/stdout med TypeScript-serverprocessen och använder en JSON-nyttolast inspirerad av V8-felsökningsprotokollet för begäranden och svar. TypeScript-servern har integrerats i Plugin-programmet TypeScript Sublime och VS Code för omfattande TypeScript-redigering.
Efter att ha integrerat två olika språkservrar började VS Code-teamet utforska ett gemensamt språkserverprotokoll för redigerare och IDE:er. Ett gemensamt protokoll gör det möjligt för en språkprovider att skapa en enskild språkserver som kan användas av olika IDE:er. En språkserverkonsument behöver bara implementera klientsidan av protokollet en gång. Detta resulterar i en win-win-situation för både språkleverantören och språkkonsumenten.
Språkserverprotokollet började med det protokoll som används av TypeScript-servern och expanderade det med fler språkfunktioner inspirerade av VS Code-språk-API:et. Protokollet backas upp med JSON-RPC för fjärranrop på grund av dess enkelhet och befintliga bibliotek.
VS Code-teamet prototyperade protokollet genom att implementera flera linterspråkservrar som svarar på förfrågningar om lint (genomsökning) en fil och returnerar en uppsättning identifierade varningar och fel. Målet var att "lint" en fil medan användaren redigerar i ett dokument, vilket innebär att det kommer att finnas många "linting"-förfrågningar i en redigeringssession. Det var klokt att hålla en server igång så att en ny lintningsprocess inte behövde startas för varje användarredigering. Flera linter-servrar implementerades, inklusive VS Code-tilläggen ESLint och TSLint. Dessa två linter-servrar implementeras båda i TypeScript/JavaScript och körs på Node.js. De delar ett bibliotek som implementerar klient- och serverdelen av protokollet.
Så här fungerar LSP
En språkserver körs i sin egen process och verktyg som Visual Studio eller VS Code kommunicerar med servern med hjälp av språkprotokollet via JSON-RPC. En annan fördel med språkservern som körs i en dedikerad process är att prestandaproblem som rör en enskild processmodell undviks. Den faktiska transportkanalen kan antingen vara stdio, sockets, namngivna pipes eller nod-ipc om både klienten och servern är skrivna i Node.js.
Nedan visas ett exempel på hur ett verktyg och en språkserver kommunicerar under en rutinmässig redigeringssession:
Användaren öppnar en fil (kallas för ett dokument) i verktyget: Verktyget meddelar språkservern att ett dokument är öppet ("textDocument/didOpen"). Från och med nu finns sanningen om innehållet i dokumentet inte längre i filsystemet utan behålls av verktyget i minnet.
Användaren gör ändringar: Verktyget meddelar servern om dokumentändringen ("textDocument/didChange") och den semantiska informationen i programmet uppdateras av språkservern. När detta händer analyserar språkservern den här informationen och meddelar verktyget med de identifierade felen och varningarna ("textDocument/publishDiagnostics").
Användaren kör "Gå till definition" på en symbol i redigeraren: Verktyget skickar en "textDocument/definition"-begäran med två parametrar: (1) dokument-URI:n och (2) textpositionen varifrån begäran Gå till definition initierades till servern. Servern svarar med dokument-URI:n och positionen för symbolens definition i dokumentet.
Användaren stänger dokumentet (filen): Ett "textDocument/didClose"-meddelande skickas från verktyget, vilket informerar språkservern om att dokumentet inte längre finns i minnet och att det aktuella innehållet nu är uppdaterat i filsystemet.
Det här exemplet illustrerar hur protokollet kommunicerar med språkservern på nivån för redigeringsfunktioner som "Gå till definition", "Hitta alla referenser". De datatyper som används av protokollet är redigerare eller IDE-datatyper som det öppna textdokumentet och markörens position. Datatyperna är inte på nivån för en programmeringsspråkdomänmodell som vanligtvis skulle ge abstrakta syntaxträd och kompilatorsymboler (till exempel lösta typer, namnområden, ...). Detta förenklar protokollet avsevärt.
Nu ska vi titta närmare på begäran "textDocument/definition". Nedan visas de nyttolaster som går mellan klientverktyget och språkservern för begäran "Gå till definition" i ett C++-dokument.
Det här är begäran:
{
"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
}
}
}
Det här är svaret:
{
"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
}
}
}
}
I efterhand är en beskrivning av datatyperna på redigerarens nivå snarare än på nivån för programmeringsspråkmodellen en av orsakerna till att språkserverprotokollet lyckades. Det är mycket enklare att standardisera en textdokument-URI eller en markörposition jämfört med att standardisera ett abstrakt syntaxträd och kompilatorsymboler för olika programmeringsspråk.
När en användare arbetar med olika språk startar VS Code vanligtvis en språkserver för varje programmeringsspråk. Exemplet nedan visar en session där användaren arbetar med Java- och SASS-filer.
Capabilities
Alla språkservrar har inte stöd för alla funktioner som definieras av protokollet. Därför anger klienten och servern sina stödda funktioner genom 'funktioner'. Till exempel meddelar en server att den kan hantera begäran "textDocument/definition", men den kanske inte hanterar begäran om arbetsyta/symbol. På samma sätt kan klienter meddela att de kan tillhandahålla "på väg att spara" meddelanden innan ett dokument sparas, så att en server kan beräkna textredigeringar för att automatiskt formatera det redigerade dokumentet.
Integrera en språkserver
Den faktiska integreringen av en språkserver i ett visst verktyg definieras inte av språkserverprotokollet och lämnas till verktygsimpletörerna. Vissa verktyg integrerar språkservrar allmänt genom att ha ett tillägg som kan starta och prata med alla typer av språkservrar. Andra, till exempel VS Code, skapar ett anpassat tillägg per språkserver, så att ett tillägg fortfarande kan tillhandahålla vissa anpassade språkfunktioner.
För att förenkla implementeringen av språkservrar och klienter finns det bibliotek eller SDK:er för klient- och serverdelarna. Dessa bibliotek tillhandahålls för olika språk. Det finns till exempel en npm-modul för språkklient för att underlätta integreringen av en språkserver i ett VS Code-tillägg och en annan språkserver npm-modul för att skriva en språkserver med hjälp av Node.js. Det här är den aktuella listan av stöd bibliotek.
Använda Language Server Protocol i Visual Studio
- Lägga till ett Language Server Protocol-tillägg – Lär dig mer om att integrera en språkserver i Visual Studio.