VSCode Server 模塊設計通過之前的介紹我們可以了解到,VS Code 的能" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > VS Code For Web 深入淺出 -- Server 模塊設計篇

VS Code For Web 深入淺出 -- Server 模塊設計篇

時間:2023-09-26 02:24:01 | 來源:網(wǎng)站運營

時間:2023-09-26 02:24:01 來源:網(wǎng)站運營

VS Code For Web 深入淺出 -- Server 模塊設計篇:在了解了 VS Code 的通信機制后,我們可以著手分析 VS Code Server 中各模塊的實現(xiàn)以及設計思路了。

VSCode Server 模塊設計

通過之前的介紹我們可以了解到,VS Code 的能力是前后端分離的,這使得 remote server 的改造實現(xiàn)變得簡單。







通過這一張架構(gòu)圖,我們可以直觀的看到在 VS Code 中,前后端能力職責的劃分。

可以看出,除了少數(shù)的一些像本地文件上傳,語法高亮、主題設置等能力,一些重依賴多進程通信、OS支持,語言編譯的能力都被設計在了 Server 端中,以保證 Client 端足夠的輕量簡潔,可以運行在 Web 這樣的輕環(huán)境中。

在本篇中,我來帶大家淺要分析 Server 端的幾個重要模塊的設計思路與實現(xiàn)。

Remote File System 設計

Remote File System 負責處理文件系統(tǒng)的讀寫操作,同時還需要處理文件系統(tǒng)的變化事件,以便于客戶端能夠?qū)崟r更新文件系統(tǒng)的變化。在 VSCode 中,它封裝了一層 Virtual file system 來實現(xiàn)對不同文件系統(tǒng)的兼容控制。

這一部分是 VSCode Server 的核心中最容易實現(xiàn)的部分。它本質(zhì)上就是使依賴現(xiàn)代瀏覽器的 File_System_Access_API 來實現(xiàn)的(強制在 HTTPS 下使用)。

async function getTheFile() { // open file picker [fileHandle] = await window.showOpenFilePicker(pickerOpts); // get file contents const fileData = await fileHandle.getFile();}具體的代碼聲明位置見 FileSystemProvider。

這里使用 vscode-vfs 這個庫來實現(xiàn)虛擬文件系統(tǒng)。這是一個 URI 方案,它注冊了 File System Provider,并且該文件系統(tǒng)上的資源將由使用該模式的 URI 表示(例如vscode-vfs://vscode/package.json)。

因此,直接打開遠程存儲庫也得以實現(xiàn),例如 Github Codespaces 的打開就是這樣實現(xiàn)的。

使用 vscode-vfs://github/microsoft/vscode, 通過訪問 https://github.com/microsoft/vscode,就能夠在不進行 git clone的情況下,直接打開項目文件夾了。

實例化后,全局都可以通過傳入 RuntimeEnvironment,通過 runtime.fs 來訪問與調(diào)用。

async stat(uri: string): Promise<FileStat> { if (fileFs && uri.startsWith('file:')) { return fileFs.stat(uri); } const res = await connection.sendRequest(FsStatRequest.type, uri.toString()); return res;}readDirectory(uri: string): Promise<[string, FileType][]> { if (fileFs && uri.startsWith('file:')) { return fileFs.readDirectory(uri); } return connection.sendRequest(FsReadDirRequest.type, uri.toString());}當然,對于不支持這套 API 的瀏覽器來說,打開時會檢測接口,彈出警告。







至于解決方式,之前說過,VSCode 的 server 端是同構(gòu)的,server 自然也能提供本地文件系統(tǒng)支持,仍可以通過瀏覽器的上傳 API 來實現(xiàn)。

Remote Terminal Process 設計







這里實際上是復用了VSCode 之前推出的 Remote-Server extension 能力,通過 SSH 隧道的方式,將終端的輸入輸出流轉(zhuǎn)發(fā)到遠程服務器上。(再一次說明了為什么強制要求在HTTPS下使用)

還記得我們之前提到過的,Channel 為通信的最小單元嗎?VSCode ServerRemote Terminal 就是通過一個 RemoteTerminalChannel 來實現(xiàn)的。

通過監(jiān)聽與觸發(fā)不同的事件(如onExecuteCommand, sendCommandResult),來實現(xiàn)對 Remote Terminal 的不同行為的信息同步。

async call(ctx: RemoteAgentConnectionContext, command: string, args?: any): Promise<any> { switch (command) { case '$restartPtyHost': return this._ptyService.restartPtyHost?.apply(this._ptyService, args); case '$createProcess': { const uriTransformer = createURITransformer(ctx.remoteAuthority); return this._createProcess(uriTransformer, <ICreateTerminalProcessArguments>args); } case '$attachToProcess': return this._ptyService.attachToProcess.apply(this._ptyService, args); case '$detachFromProcess': return this._ptyService.detachFromProcess.apply(this._ptyService, args); case '$listProcesses': return this._ptyService.listProcesses.apply(this._ptyService, args); case '$orphanQuestionReply': return this._ptyService.orphanQuestionReply.apply(this._ptyService, args); case '$acceptPtyHostResolvedVariables': return this._ptyService.acceptPtyHostResolvedVariables?.apply(this._ptyService, args); case '$start': return this._ptyService.start.apply(this._ptyService, args); case '$input': return this._ptyService.input.apply(this._ptyService, args); case '$acknowledgeDataEvent': return this._ptyService.acknowledgeDataEvent.apply(this._ptyService, args); case '$shutdown': return this._ptyService.shutdown.apply(this._ptyService, args); case '$resize': return this._ptyService.resize.apply(this._ptyService, args); case '$getInitialCwd': return this._ptyService.getInitialCwd.apply(this._ptyService, args); case '$getCwd': return this._ptyService.getCwd.apply(this._ptyService, args); case '$processBinary': return this._ptyService.processBinary.apply(this._ptyService, args); case '$sendCommandResult': return this._sendCommandResult(args[0], args[1], args[2]); case '$installAutoReply': return this._ptyService.installAutoReply.apply(this._ptyService, args); case '$uninstallAllAutoReplies': return this._ptyService.uninstallAllAutoReplies.apply(this._ptyService, args); case '$getDefaultSystemShell': return this._getDefaultSystemShell.apply(this, args); case '$getProfiles': return this._getProfiles.apply(this, args); case '$getEnvironment': return this._getEnvironment(); case '$getWslPath': return this._getWslPath(args[0]); case '$getTerminalLayoutInfo': return this._ptyService.getTerminalLayoutInfo(<IGetTerminalLayoutInfoArgs>args); case '$setTerminalLayoutInfo': return this._ptyService.setTerminalLayoutInfo(<ISetTerminalLayoutInfoArgs>args); case '$serializeTerminalState': return this._ptyService.serializeTerminalState.apply(this._ptyService, args); case '$reviveTerminalProcesses': return this._ptyService.reviveTerminalProcesses.apply(this._ptyService, args); case '$getRevivedPtyNewId': return this._ptyService.getRevivedPtyNewId.apply(this._ptyService, args); case '$setUnicodeVersion': return this._ptyService.setUnicodeVersion.apply(this._ptyService, args); case '$reduceConnectionGraceTime': return this._reduceConnectionGraceTime(); case '$updateIcon': return this._ptyService.updateIcon.apply(this._ptyService, args); case '$updateTitle': return this._ptyService.updateTitle.apply(this._ptyService, args); case '$updateProperty': return this._ptyService.updateProperty.apply(this._ptyService, args); case '$refreshProperty': return this._ptyService.refreshProperty.apply(this._ptyService, args); case '$requestDetachInstance': return this._ptyService.requestDetachInstance(args[0], args[1]); case '$acceptDetachedInstance': return this._ptyService.acceptDetachInstanceReply(args[0], args[1]); case '$freePortKillProcess': return this._ptyService.freePortKillProcess?.apply(this._ptyService, args); } throw new Error(`IPC Command ${command} not found`); } listen(_: any, event: string, arg: any): Event<any> { switch (event) { case '$onPtyHostExitEvent': return this._ptyService.onPtyHostExit || Event.None; case '$onPtyHostStartEvent': return this._ptyService.onPtyHostStart || Event.None; case '$onPtyHostUnresponsiveEvent': return this._ptyService.onPtyHostUnresponsive || Event.None; case '$onPtyHostResponsiveEvent': return this._ptyService.onPtyHostResponsive || Event.None; case '$onPtyHostRequestResolveVariablesEvent': return this._ptyService.onPtyHostRequestResolveVariables || Event.None; case '$onProcessDataEvent': return this._ptyService.onProcessData; case '$onProcessReadyEvent': return this._ptyService.onProcessReady; case '$onProcessExitEvent': return this._ptyService.onProcessExit; case '$onProcessReplayEvent': return this._ptyService.onProcessReplay; case '$onProcessOrphanQuestion': return this._ptyService.onProcessOrphanQuestion; case '$onExecuteCommand': return this.onExecuteCommand; case '$onDidRequestDetach': return this._ptyService.onDidRequestDetach || Event.None; case '$onDidChangeProperty': return this._ptyService.onDidChangeProperty; default: break; } throw new Error('Not supported'); }

Extension Processes 設計

存儲位置

VSCode Server 會將通過 code-server --install-extension <extension id> 命令安裝的 extensions 存儲在 $XDG_DATA_HOME/code-server/extensions 下。

用戶配置信息存儲在本地的~/.vscode 下,使用官方的 Settings Sync 插件進行配置漫游。

插件分類

VSCode 將插件分為了 UI ExtensionWorkspace Extension 兩種,通過 extensionKind 字段進行指定。

如果不涉及到 Node.js 調(diào)用的簡單插件,是純聲明性質(zhì)的代碼的話(例如 Themes、key-binding,或者能直接利用客戶端 API 能覆蓋能力的插件等),則可以定義為 UI Extension,直接在客戶端中執(zhí)行,服務端只保存插件的配置信息,無需進行通信。

這也是為什么 vscode.dev 中(截至目前位置,該網(wǎng)頁不包含 Server 能力),所有的主題、包括例如 TS、Python、Markdown、HTML 等語言的文件補全、語法高亮、括號著色都是可以正常使用的原因。因為在架構(gòu)上,這些能力都是由客戶端的內(nèi)置插件(語言補全等相關特性是通過專門編寫的 web worker thread 旁路執(zhí)行)提供的,通過 VSCode API 直接進行調(diào)用。

但如果功能涉及到運行時的系統(tǒng)級調(diào)用,則需要被定義為Workspace Extension,它可以完全訪問源碼、文件系統(tǒng)、以及大部分 OS API

Workspace Extension 需要安裝在服務端,并需要在插件中顯式聲明。

體現(xiàn)在編碼規(guī)范上,我們需要為插件項目的 package.json 文件中添加 main 的 entrypoint,以執(zhí)行服務端插件調(diào)用,而 UI Extension 的 entrypoint 使用 browser 表示。

{ ... "main": "./dist/node/extension.js", "browser": "./dist/browser/extension.js", "capabilities": { "virtualWorkspaces": true } ...}邏輯上,插件需要根據(jù) web 端與 server 端分開編寫,并自行做好兼容。






關鍵詞:設計,深入

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關閉