国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器

這篇具有很好參考價(jià)值的文章主要介紹了如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

對(duì)于一個(gè)云開發(fā)平臺(tái)來說,一個(gè)好的 Web IDE 能很大程度地提高用戶的編碼體驗(yàn),而一個(gè) Web IDE 的一個(gè)重要組成部分就是代碼編輯器。

目前有著多款 web 上的代碼編輯器可供選擇,比如 Ace、CodeMirror、Monaco,這三款編輯器的比較在這篇文章中有著詳細(xì)的介紹,在此就不作過多贅述。這篇文章我們選擇 Monaco Editor 來對(duì) LSP 進(jìn)行集成,從而在理論上能夠支持所有的編程語言。

原文鏈接:https://forum.laf.run/d/1027

什么是 LSP

如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器

LSP(Language Server Protocol),也就是語言服務(wù)協(xié)議,更具體更通俗地說就是定義了在代碼編輯器和語言服務(wù)器之間的一套規(guī)范,從而讓原本

m 個(gè)編輯器與 n 個(gè)編程語言之間的對(duì)應(yīng)關(guān)系

變?yōu)?/p>

m 個(gè)編輯器與 LSP 的關(guān)系和 n 個(gè)編程語言與 LSP 之間的關(guān)系,

從而將開發(fā)的復(fù)雜度由 m*n 降到了 m+n

除了對(duì)編輯器開發(fā)者和編程語言開發(fā)者友好,對(duì)我們這種嘗試讓一個(gè)編輯器支持多種語言的開發(fā)者也更是友好,有 vscode 這樣的編輯器珠玉在前,便能輕松地根據(jù) vscode 的設(shè)計(jì)思路實(shí)現(xiàn)我們的需求。

預(yù)覽

在這篇文章中,我們會(huì)開發(fā)一個(gè)最小最輕量的編輯器 Demo 作為演示,架構(gòu)非常簡(jiǎn)單,就是前端創(chuàng)建一個(gè) Monaco Editor,后端創(chuàng)建一個(gè)語言服務(wù)器,二者之間通過 vscode-ws-jsonrpcWebSocket 服務(wù)進(jìn)行傳輸,實(shí)際實(shí)現(xiàn)的 WebPython 編輯器如下:

如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器

Server 端開發(fā)

Web 端能接入語言服務(wù)前,我們得先在服務(wù)端運(yùn)行一個(gè)語言服務(wù),https://langserver.org/ 這個(gè)網(wǎng)站收錄了許多語言服務(wù)的實(shí)現(xiàn),

如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器

這里我們選擇微軟官方維護(hù)的 pyright 提供語言服務(wù)

首先創(chuàng)建 Express 服務(wù)器,配置靜態(tài)文件服務(wù),使用 fileURLToPathdirname 來獲取當(dāng)前文件的路徑,并將服務(wù)設(shè)置在 30000 端口

const app = express();
const __filename = fileURLToPath(import.meta.url);
const dir = dirname(__filename);
app.use(express.static(dir));
const server = app.listen(30000);

然后我們需要?jiǎng)?chuàng)建一個(gè) WebSocket Server,注意這里的 noServer 參數(shù),如果沒有指定 noServer,那么 WebSocketServer 會(huì)自動(dòng)創(chuàng)建一個(gè) http server 來處理瀏覽器的 HTTP 請(qǐng)求到 WebSocket 請(qǐng)求的 upgrade。

const wss = new WebSocketServer({
	noServer: true,
});

而這里我們需要?jiǎng)?chuàng)建自己的 HTTP 服務(wù)器,并手動(dòng)處理瀏覽器的 upgrade 請(qǐng)求。下面代碼便是如何監(jiān)聽 upgrade 事件并進(jìn)行處理。

server.on('upgrade',()=>{});

在處理函數(shù)中,按照下面的代碼將 WebSocket 使用到 jsonrpc 協(xié)議中,并啟動(dòng)語言服務(wù)器讓二者相連。

先構(gòu)建語言服務(wù)器的路徑,找到 pyright 包所在的位置。

const baseDir = resolve(getLocalDirectory(import.meta.url));
const relativeDir = '../../../node_modules/pyright/dist/pyright-langserver.js';
const ls = resolve(baseDir, relativeDir); 

再創(chuàng)建語言服務(wù)器的連接 和 創(chuàng)建 WebSocket 的數(shù)據(jù)連接

const serverConnection = createServerProcess(serverName, 'node', [ls, '--stdio']);
const reader = new WebSocketMessageReader(socket);
const writer = new WebSocketMessageWriter(socket);
const socketConnection = createConnection(reader, writer, () => socket.dispose());

最后用 forward 函數(shù)將消息從 soketConnection 轉(zhuǎn)發(fā)到 serverConnection,如下:

forward(socketConnection, serverConnection, message => {
    if (Message.isRequest(message)) {
        console.log(`Received:`);
        console.log(message);
	}
    if (Message.isResponse(message)) {
        console.log(`Sent:`);
        console.log(message);
    }
	return message;
});

于是我們將語言服務(wù)器跑起來,并在文章后面階段會(huì)寫的前端編輯器中隨便輸入一點(diǎn)東西,可以看到終端里輸出了 message,

如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器

這樣我們就使用 pyright 完成了語言服務(wù)器的開發(fā)。

Web 端開發(fā)

接下來我們開發(fā)前端的內(nèi)容,還是在上面的那個(gè)網(wǎng)站中,

如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器

可以看到 Monaco Editor 也是有支持 LSP 的方案的,所以我們使用 TypeFox 開發(fā)的 monaco-languageclient 對(duì)monaco 進(jìn)行集成 lsp 的開發(fā)。

首先使用 monaco-languageclient 的 initServices 函數(shù)初始化一些服務(wù),其中最重要的就是下面四個(gè)配置,定義了 Monaco 的語言服務(wù)與主題顯示。

await initServices({
    enableModelService: true,
    enableThemeService: true,
    enableTextmateService: true,
    enableLanguagesService: true,
})

然后創(chuàng)建能夠與語言服務(wù)器相連的 WebSocket 連接。

createWebSocket("ws://localhost:30000/pyright");

而創(chuàng)建這個(gè)連接也需要用到 monaco-languageclient。

const createWebSocket = (url: string): WebSocket => {
    const webSocket = new WebSocket(url);
    webSocket.onopen = async () => {
        const socket = toSocket(webSocket);
        const reader = new WebSocketMessageReader(socket);
        const writer = new WebSocketMessageWriter(socket);
        languageClient = createLanguageClient({
            reader,
            writer
        });
        await languageClient.start();
        reader.onClose(() => languageClient.stop());
    };
    return webSocket;
};

const createLanguageClient = (transports: MessageTransports): MonacoLanguageClient => {
    return new MonacoLanguageClient({
        name: 'Pyright Language Client',
        clientOptions: {
            documentSelector: [languageId],
            errorHandler: {
                error: () => ({ action: ErrorAction.Continue }),
                closed: () => ({ action: CloseAction.DoNotRestart })
            },
            workspaceFolder: {
                index: 0,
                name: 'workspace',
                uri: monaco.Uri.parse('/tmp')
            },
            synchronize: {
                fileEvents: [vscode.workspace.createFileSystemWatcher('**')]
            }
        },
        connectionProvider: {
            get: () => {
                return Promise.resolve(transports);
            }
        }
    });
};

接下來這里需要?jiǎng)?chuàng)建一個(gè)虛擬文件系統(tǒng),作為 Monaco Editor 實(shí)例的輸入輸出。

const fileSystemProvider = new RegisteredFileSystemProvider(false);
fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/test.py'), 'print("Hello, laf!")'));
registerFileSystemOverlay(1, fileSystemProvider);
const modelRef = await createModelReference(monaco.Uri.file('/test.py'));

最后創(chuàng)建 Monaco Editor 實(shí)例即可,還能進(jìn)行些許的配置。

createConfiguredEditor(document.getElementById('container')!, {
    model: modelRef.object.textEditorModel,
    automaticLayout: true,
    minimap: {
    	enabled: false
    },
    scrollbar: {
        verticalScrollbarSize: 4,
        horizontalScrollbarSize: 8,
    },
    overviewRulerLanes: 0,
    lineNumbersMinChars: 4,
    scrollBeyondLastLine: false,
    theme: 'vs',
});

就這樣我們也完成了前端。

import Editor from './python/Editor';

function App() {
  
  return (
    <>
      <h2>monaco python lsp</h2>
      <div style={{height:"500px", width:"800px", border:"1px solid black", padding:"8px 0"}}>
        <Editor />
      </div>
    </>
  )
}

export default App

效果如下

如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器

小結(jié)

要深入理解 LSP 以及其背后的工作原理還是有很大的難度的,但是好在有 languageserver,languageclient 這類優(yōu)秀的開源項(xiàng)目提供支持,能夠讓我們?cè)趦H僅拼湊了幾段代碼后擁有不錯(cuò)的代碼編輯器效果。下一步計(jì)劃用 LSP 改造 Laf 的 Web IDE。

由于我也只是剛剛接觸這塊知識(shí),文章中難免有錯(cuò)漏,希望能與讀到這里的各位共同交流進(jìn)步。文章來源地址http://www.zghlxwxcb.cn/news/detail-705994.html

參考資料

  • https://github.com/microsoft/language-server-protocol/wiki/Protocol-History
  • https://medium.com/@malintha1996/understanding-the-language-server-protocol-5c0ba3ac83d2
  • https://ubug.io/blog/workpad-part-6
  • https://www.typefox.io/blog/how-to-embed-a-monaco-editor-in-a-browser-as-a-part-of-my-first-task-at-typefox
  • https://www.typefox.io/blog/teaching-the-language-server-protocol-to-microsofts-monaco-editor

到了這里,關(guān)于如何創(chuàng)建集成 LSP 支持多語言的 Web 代碼編輯器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包