vscode config

This commit is contained in:
maxscout
2025-12-15 12:50:23 -05:00
parent 581fa1269b
commit e4ecfe69e1
213 changed files with 19397 additions and 0 deletions

1
.local/share/code-server Symbolic link
View File

@@ -0,0 +1 @@
.vscode

21
.vscode/argv.json vendored Normal file
View File

@@ -0,0 +1,21 @@
// This configuration file allows you to pass permanent command line arguments to VS Code.
// Only a subset of arguments is currently supported to reduce the likelihood of breaking
// the installation.
//
// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT
//
// NOTE: Changing this file requires a restart of VS Code.
{
// Use software rendering instead of hardware accelerated rendering.
// This can help in cases where you see rendering issues in VS Code.
// "disable-hardware-acceleration": true,
// Allows to disable crash reporting.
// Should restart the app if the value is changed.
"enable-crash-reporter": true,
// Unique id used for correlating crash reports sent from this instance.
// Do not edit this value.
"crash-reporter-id": "d2c346d5-3324-4244-8429-4d07addc725d",
"password-store": "basic"
}

1
.vscode/extensions/.obsolete vendored Normal file
View File

@@ -0,0 +1 @@
{"coder.coder-remote-1.11.5":true}

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="vscode-neovim" Version="1.18.24" Publisher="asvetliakov" />
<DisplayName>VSCode Neovim</DisplayName>
<Description xml:space="preserve">Vim mode for VSCode, powered by Neovim</Description>
<Tags>keybindings,vi,vim,neovim</Tags>
<Categories>Other,Keymaps</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.90.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="ui,workspace" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Code.EnabledApiProposals" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExecutesCode" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/asvetliakov/vscode-neovim.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/asvetliakov/vscode-neovim.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/asvetliakov/vscode-neovim.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/asvetliakov/vscode-neovim/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/asvetliakov/vscode-neovim#readme" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/images/icon.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/readme.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/changelog.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/images/icon.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Alexey Svetliakov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,855 @@
# VSCode Neovim
<p align="center"><img src="https://github.com/asvetliakov/vscode-neovim/raw/HEAD/images/icon.png" height="128"><br>VSCode Neovim Integration</p>
<p align=center>
<a href="https://marketplace.visualstudio.com/items?itemName=asvetliakov.vscode-neovim"><img src="https://img.shields.io/visual-studio-marketplace/v/asvetliakov.vscode-neovim?color=%234c1&label=Visual%20Studio%20Marketplace"></a>
<a href="https://github.com/asvetliakov/vscode-neovim/actions/workflows/build_test.yml"><img src="https://github.com/asvetliakov/vscode-neovim/workflows/Code%20Check%20&%20Test/badge.svg"></a>
<a href="https://gitter.im/vscode-neovim/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge"><img src="https://badges.gitter.im/vscode-neovim/community.svg"></a>
</p>
[Neovim](https://neovim.io/) is a fork of Vim to allow greater extensibility and integration. This extension uses a
fully embedded Neovim instance, no more half-complete Vim emulation! VSCode's native functionality is used for insert
mode and VSCode commands, making the best use of both editors.
- 🎉 Feature-complete Vim integration (except insert-mode and some Nvim UI plugins) by utilizing Nvim as a backend.
- 🔧 Supports custom `init.lua` and most Nvim plugins.
- 🥇 First-class and lag-free insert mode, letting VSCode do what it does best.
- 🤝 Complete integration with VSCode features (lsp/autocompletion/snippets/multi-cursor/etc).
<strong>Table of Contents</strong>
- [🧰 Getting Started](#-getting-started)
- [Installation](#installation)
- [Neovim configuration](#neovim-configuration)
- [VSCode Settings \& Commands](#vscode-settings--commands)
- [💡 Tips and Features](#-tips-and-features)
- [VSCode specific differences](#vscode-specific-differences)
- [Troubleshooting](#troubleshooting)
- [Performance](#performance)
- [Composite escape keys](#composite-escape-keys)
- [Jumplist](#jumplist)
- [Multiple cursors](#multiple-cursors)
- [Remote development](#remote-development)
- [⚡️ API](#-api)
- [vscode.action(name, opts)](#vscodeactionname-opts)
- [vscode.call(name, opts, timeout)](#vscodecallname-opts-timeout)
- [vscode.on(event, callback)](#vscodeonevent-callback)
- [vscode.has_config(name)](#vscodehas_configname)
- [vscode.get_config(name)](#vscodeget_configname)
- [vscode.update_config(name, value, target)](#vscodeupdate_configname-value-target)
- [vscode.notify(msg)](#vscodenotifymsg)
- [vscode.eval(code\[, opts, timeout\])](#vscodeevalcode-opts-timeout)
- [vscode.eval_async(code\[, opts\])](#vscodeeval_asynccode-opts)
- [vscode.with_insert(callback\[, ms\])](#vscodewith_insertcallback-ms)
- [Builtin module overrides](#builtin-module-overrides)
- [VimScript](#vimscript)
- [⌨️ Keybindings (shortcuts)](#-keybindings-shortcuts)
- [Keybinding Passthroughs](#keybinding-passthroughs)
- [Insert mode control keys passthrough](#insert-mode-control-keys-passthrough)
- [Normal mode control keys passthrough](#normal-mode-control-keys-passthrough)
- [Cmdline mode special keys passthrough](#cmdline-mode-special-keys-passthrough)
- [Disable passthrough for certain filetypes](#disable-passthrough-for-certain-filetypes)
- [Remove other vscode or passthrough keybindings](#remove-other-vscode-or-passthrough-keybindings)
- [Code navigation bindings](#code-navigation-bindings)
- [Explorer/list navigation bindings](#explorerlist-navigation-bindings)
- [Explorer file manipulation bindings](#explorer-file-manipulation-bindings)
- [Hover widget manipulation bindings](#hover-widget-manipulation-bindings)
- [📟 Neovim Commands](#-neovim-commands)
- [File management](#file-management)
- [Tab management](#tab-management)
- [Buffer/window management](#bufferwindow-management)
- [🎨 Highlights](#-highlights)
- [🧰 Developing](#-developing)
- [❤️ Credits \& External Resources](#-credits--external-resources)
## 🧰 Getting Started
### Installation
Install the [vscode-neovim](https://marketplace.visualstudio.com/items?itemName=asvetliakov.vscode-neovim) extension.
Install [Neovim](https://github.com/neovim/neovim/wiki/Installing-Neovim) **0.10.0** or greater.
> **Note:** Though the extension strives to be as compatible as possible with older versions of Neovim, some older
> versions may have quirks that are not present anymore. In light of this, certain configuration settings are
> recommended in some older versions for the best experience. These can be found
> [on the wiki](https://github.com/vscode-neovim/vscode-neovim/wiki/Version-Compatibility-Notes).
\[Optional\] Set the Neovim path in the extension settings under
"`vscode-neovim.neovimExecutablePaths.win32/linux/darwin`", respective to your system. For example,
"`C:\Neovim\bin\nvim.exe`" or "`/usr/local/bin/nvim`".
> **WSL Users:** If you want to use Neovim from WSL, set the `useWSL` configuration toggle and specify the Linux path to
> the nvim binary. `wsl.exe` Windows binary and `wslpath` Linux binary are required for this. `wslpath` must be
> available through `$PATH` Linux env setting. Use `wsl --list` to check for the correct default Linux distribution.
>
> **Snap Users:** If you want to use Neovim from Snap, the Neovim path must be resolved to the snap binary location. On
> some systems it might be "`/snap/nvim/current/usr/bin/nvim`". To check if you're running as a snap package, see if
> `which nvim` resolves to `/usr/bin/snap`.
### Neovim configuration
Since many Vim plugins can cause issues in VSCode, it is recommended to start from an empty `init.vim`. For a guide for
which types of plugins are supported, see [troubleshooting](#troubleshooting).
Before creating an issue on Github, make sure you can reproduce the problem with an empty `init.vim` and no VSCode
extensions.
To determine if Neovim is running in VSCode, add to your `init.vim`:
```vim
if exists('g:vscode')
" VSCode extension
else
" ordinary Neovim
endif
```
In lua:
```lua
if vim.g.vscode then
-- VSCode extension
else
-- ordinary Neovim
end
```
To conditionally activate plugins, the best solution is to use the
[LazyVim VSCode extra](https://www.lazyvim.org/extras/vscode). However, `packer.nvim` and `lazy.nvim` have built-in
support for `cond = vim.g.vscode` and `vim-plug` has a
[few solutions](https://github.com/junegunn/vim-plug/wiki/tips#conditional-activation). See
[plugins](https://github.com/vscode-neovim/vscode-neovim/wiki/Plugins) in the wiki for tips on configuring Vim plugins.
### VSCode Settings & Commands
You can view all available [settings](https://code.visualstudio.com/docs/getstarted/settings) and commands by opening
the [vscode-neovim](https://github.com/asvetliakov/vscode-neovim/blob/HEAD/vscode:extension/asvetliakov.vscode-neovim) extension details pane, and navigating to the features
tab.
## 💡 Tips and Features
### VSCode specific differences
- File and editor management commands such as `:e`/`:q`/`:vsplit`/`:tabnext`/etc are mapped to corresponding VSCode
commands and behavior may be different ([see below](#%EF%B8%8F-keybindings-shortcuts)).
- **Do not** use vim commands like `:e` in scripts/keybindings, they won't work. If you're using them in some custom
commands/mappings, you might need to rebind them to call VSCode commands from Neovim with
`require('vscode').call()` (see [API](#%EF%B8%8F-api)).
- Since version 1.18.0, `:w`, `:wa` and `:sav` commands are supported and no longer alias to VSCode commands. You
can use them as you would in Neovim.
- When you type some commands they may be substituted for another, check
[AlterCommand](https://github.com/search?q=repo%3Avscode-neovim%2Fvscode-neovim%20AlterCommand&type=code) for the list
of substitutions.
- Scrolling is done by VSCode. <kbd>C-d</kbd>/<kbd>C-u</kbd>/etc are slightly different.
- Editor customization (relative line number, scrolloff, etc) is handled by VSCode.
- Dot-repeat (<kbd>.</kbd>) is slightly different - moving the cursor within a change range won't break the repeat.
sequence. In Neovim, if you type `abc<cursor>` in insert mode, then move the cursor to `a<cursor>bc` and type `1` here
the repeat sequence would be `1`. However, in VSCode, it would be `a1bc`. Another difference is that when you delete
some text in insert mode, dot repeat only works from right to left, meaning it will treat <kbd>Del</kbd> key as
<kbd>BS</kbd> keys when running dot repeat.
### Troubleshooting
- View the logs via `Output: Focus on Output View` and select `vscode-neovim logs`.
- **To enable debug logs,** click the "gear" icon and select `Debug`, then click it again and choose
`Set As Default`.
- Enable `vscode-neovim.neovimClean` in VSCode settings, which starts Nvim _without_ your plugins (`nvim --clean`). Nvim
plugins can do _anything_. Visual effects in particular can cause visual artifacts. vscode-neovim does its best to
merge the visual effects of Nvim and VSCode, but it's far from perfect. You may need to disable some Nvim plugins that
cause visual effects.
- If you encounter rendering issues (visual artifacts), try <kbd>CTRL-L</kbd> to force Nvim to redraw.
- If you get the `Unable to init vscode-neovim: command 'type' already exists` message, uninstall other VSCode
extensions that use `registerTextEditorCommand("type", …)` (like
[VSCodeVim](https://marketplace.visualstudio.com/items?itemName=vscodevim.vim) or
[Overtype](https://marketplace.visualstudio.com/items?itemName=adammaras.overtype)).
- On a Mac, the <kbd>h</kbd>, <kbd>j</kbd>, <kbd>k</kbd> and <kbd>l</kbd> movement keys may not repeat when held, to fix
this open Terminal and execute the following command:
`defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false`.
- To fix the remapped escape key not working in Linux, set `"keyboard.dispatch": "keyCode"`.
- Two VSCode developer commands are useful for keybindings debugging:
- `Developer: Toggle Keyboard Shortcuts Troubleshooting` for tracing VSCode emitted keypresses and their processing
via defined keybindings.
- `Developer: Inspect Key Mapping` for getting the recognized mappings for the current keyboard layout inside
VSCode.
### Performance
If you have any performance problems (cursor jitter usually) make sure you're not using vim plugins that increase
latency and cause performance problems.
Make sure to disable unneeded plugins, as many of them don't make sense with VSCode. Specifically, you don't need any
code highlighting, completion, LSP plugins, or plugins that spawn windows/buffers (nerdtree , fuzzy-finders, etc). Most
navigation/textobject/editing plugins should be fine.
For example, make sure you're not using anything that renders decorators very often:
- Line number extensions (VSCode has built-in support for normal/relative line numbers)
- Indent guide extensions (VSCode has built-in indent guides)
- Brackets highlighter extensions (VSCode has built-in feature)
> If you're not sure, disable all other extensions, **reload VSCode window**, and see if the problem persists before
> reporting it.
### Composite escape keys
Set with `compositeKeys` and tweak with `compositeTimeout`.
Examples: add to your `settings.json`:
<kbd>jj</kbd> to escape
```jsonc
{
"vscode-neovim.compositeKeys": {
"jj": {
"command": "vscode-neovim.escape",
},
},
}
```
<kbd>jk</kbd> to escape and save
```jsonc
{
"vscode-neovim.compositeKeys": {
"jk": {
// Use lua to execute any logic
"command": "vscode-neovim.lua",
"args": [
[
"local code = require('vscode')",
"code.action('vscode-neovim.escape')",
"code.action('workbench.action.files.save')",
],
],
},
},
}
```
### Jumplist
VSCode's jumplist is used instead of Neovim's. This is to make VSCode native navigation (mouse click, jump to
definition, etc) navigable through the jumplist.
Make sure to bind to `workbench.action.navigateBack` / `workbench.action.navigateForward` if you're using custom
mappings. Marks (both upper & lowercased) should work fine.
### Multiple cursors
Multiple cursors work in:
1. Insert mode
2. Visual line mode
3. Visual block mode
To spawn multiple cursors from visual line/block modes type <kbd>ma</kbd>/<kbd>mA</kbd> or <kbd>mi</kbd>/<kbd>mI</kbd>
(by default). The effect differs:
- For visual line mode, <kbd>mi</kbd> will start insert mode on each selected line on the first non whitespace character
and <kbd>ma</kbd> will on the end of line.
- For visual block mode, <kbd>mi</kbd> will start insert on each selected line before the cursor block and <kbd>ma</kbd>
after.
- <kbd>mA</kbd>/<kbd>mI</kbd> versions accounts for empty lines (only for visual line mode, for visual block mode
they're same as <kbd>ma</kbd>/<kbd>mi</kbd>).
See gif in action:
![multicursor](https://github.com/vscode-neovim/vscode-neovim/assets/47070852/72b3f2b3-6d80-4ace-b6f4-b211baad74d1)
> **Note**: The built-in multi-cursor support may not meet your needs. Please refer to the plugin
> [vscode-multi-cursor.nvim](https://github.com/vscode-neovim/vscode-multi-cursor.nvim) for more multi-cursor features
### Remote development
We intend to use vscode-neovim as a UI extension, so when you're using remote development, vscode-neovim is enabled in
the Local Extension Host, and it should work out of the box.
If you prefer to use the remote environment's copy of Neovim, rather than the locally installed one, vscode-neovim
should be installed in the Remote Extension Host. You can set the following in your VSCode `settings.json`:
```json
{
"remote.extensionKind": {
"asvetliakov.vscode-neovim": ["workspace"]
}
}
```
> **Note**: You will need to install neovim in the remote environment.
For more information:
- [Remote Development](https://code.visualstudio.com/docs/remote/remote-overview)
- [Extension Host](https://code.visualstudio.com/api/advanced-topics/extension-host)
- [Remote Extensions](https://code.visualstudio.com/api/advanced-topics/remote-extensions)
## ⚡️ API
Load the module:
```lua
local vscode = require('vscode')
```
<!-- prettier-ignore-start -->
> [!TIP]
> The previously used module named "vscode-neovim" is now deprecated, so don't be confused if "vscode-neovim" is used in old discussions or other resources.
<!-- prettier-ignore-end -->
1. `vscode.action()`: asynchronously executes a vscode command.
2. `vscode.call()`: synchronously executes a vscode command.
3. `vscode.on()`: defines a handler for some Nvim UI events.
4. `vscode.has_config()`: checks if a vscode setting exists.
5. `vscode.get_config()`: gets a vscode setting value.
6. `vscode.update_config()`: sets a vscode setting.
7. `vscode.notify()`: shows a vscode message (see also Nvim's `vim.notify`).
8. `vscode.to_op()`: A helper for `map-operator`. See [code_actions.lua](https://github.com/asvetliakov/vscode-neovim/blob/HEAD/runtime/vscode/code_actions.lua) for the
usage
9. `g:vscode_clipboard`: Clipboard provider using VSCode's clipboard API. Used by default when in WSL. See
`:h g:clipboard` for more details. Usage: `vim.g.clipboard = vim.g.vscode_clipboard`
10. `vscode.eval()`: evaluate javascript synchronously in vscode and return the result
11. `vscode.eval_async()`: evaluate javascript asynchronously in vscode
12. `vscode.with_insert()`: perform operations in insert mode.
### vscode.action(name, opts)
Asynchronously executes a vscode command.
Parameters:
- `name` (string): The name of the action, generally a vscode command.
- `opts` (table): Map of optional parameters:
- `args` (table): List of arguments passed to the vscode command. If the command only requires a single object
parameter, you can directly pass in a map-like table.
- Examples:
- `action('foo', { args = { 'foo', 'bar', … } })`
- `action('foo', { args = { foo = bar, … } })`
- `range` (table): Specific range for the action. Implicitly passed in visual mode. Has three possible forms (all
values are 0-indexed):
- `[start_line, end_line]`
- `[start_line, start_character, end_line, end_character]`
- `{start = { line = start_line, character = start_character}, end = { line = end_line, character = end_character}}`
- `restore_selection` (boolean): Whether to preserve the current selection. Only valid when `range` is specified.
Defaults to `true`.
- `callback`: Function to handle the action result. Must have this signature: `function(err: string|nil, ret: any)`:
- `err` is the error message, if any
- `ret` is the result
- If no callback is provided, error will be shown as a VSCode notification.
Example: open definition aside (default binding):
```vim
nnoremap <C-w>gd <Cmd>lua require('vscode').action('editor.action.revealDefinitionAside')<CR>
```
Example: find in files for word under cursor (see the
[vscode command definition](https://github.com/microsoft/vscode/blob/43b0558cc1eec2528a9a1b9ee1c7a559823bda31/src/vs/workbench/contrib/search/browser/searchActionsFind.ts#L177-L197)
for the expected parameter format):
```vim
nnoremap ? <Cmd>lua require('vscode').action('workbench.action.findInFiles', { args = { query = vim.fn.expand('<cword>') } })<CR>
```
Example: use in lua script:
```lua
-- Format current document
vscode.action("editor.action.formatDocument")
do -- Comment the three lines below the cursor
local curr_line = vim.fn.line(".") - 1 -- 0-indexed
vscode.action("editor.action.commentLine", {
range = { curr_line + 1, curr_line + 3 },
})
end
do -- Comment the previous line
local curr_line = vim.fn.line(".") - 1 -- 0-indexed
local prev_line = curr_line - 1
if prev_line >= 0 then
vscode.action("editor.action.commentLine", {
range = { prev_line , prev_line },
})
end
end
do -- Find in files for word under cursor
vscode.action("workbench.action.findInFiles", {
args = { query = vim.fn.expand('<cword>') }
})
end
```
Currently, two built-in actions are provided for testing purposes:
1. `_ping` returns `"pong"`
2. `_wait` waits for the specified milliseconds and then returns `"ok"`
```lua
do -- Execute _ping asynchronously and print the result
vscode.action("_ping", {
callback = function(err, res)
if err == nil then
print(res) -- outputs: pong
end
end,
})
end
```
### vscode.call(name, opts, timeout)
Synchronously executes a vscode command.
Parameters:
- `name` (string): The name of the action, generally a vscode command.
- `opts` (table): Same as [vscode.action()](#vscodeactionname-opts).
- `timeout` (number): Timeout in milliseconds. The default value is -1, which means there is no timeout.
Returns: the result of the action
Example: format selection (default binding):
```vim
xnoremap = <Cmd>lua require('vscode').call('editor.action.formatSelection')<CR>
nnoremap = <Cmd>lua require('vscode').call('editor.action.formatSelection')<CR><Esc>
nnoremap == <Cmd>lua require('vscode').call('editor.action.formatSelection')<CR>
```
Example: use in lua script:
```lua
-- Execute _ping synchronously and print the result
print(vscode.call("_ping")) -- outputs: pong
-- Wait for 1 second and print the return value 'ok'
print(vscode.call("_wait", { args = { 1000 } })) -- outputs: ok
-- Wait for 2 seconds with a timeout of 1 second
print(vscode.call("_wait", { args = { 2000 } }), 1000)
-- error: Call '_wait' timed out
```
### vscode.on(event, callback)
Currently no available events for user use.
### vscode.has_config(name)
Check if configuration has a certain value.
Parameters:
- `name` (string|string[]): The configuration name or an array of configuration names.
Returns:
- `boolean|boolean[]`: Returns `true` if the configuration has a certain value, `false` otherwise. If `name` is an
array, returns an array of booleans indicating whether each configuration has a certain value or not.
Examples:
```lua
-- Check if the configuration "not.exist" exists
print(vscode.has_config("not.exist"))
-- Should return: false
-- Check multiple configurations
vim.print(vscode.has_config({ "not.exist", "existing.config" }))
-- Should return: { false, true }
```
### vscode.get_config(name)
Get configuration value.
Parameters:
- `name` (string|string[]): The configuration name or an array of configuration names.
Returns:
- `unknown|unknown[]`: The value of the configuration. If `name` is an array, returns an array of values corresponding
to each configuration.
Examples:
```lua
-- Get the value of "editor.tabSize"
print(vscode.get_config("editor.tabSize")) -- a number
-- Get multiple configurations
vim.print(vscode.get_config({ "editor.fontFamily", "editor.tabSize" }))
-- Should return: { "the font family", "the editor tabSizse" }
```
### vscode.update_config(name, value, target)
Update configuration value.
Parameters:
- `name` (string|string[]): The configuration name or an array of configuration names.
- `value` (unknown|unknown[]): The new value for the configuration.
- `target` ("global"|"workspace"|"workspace_folder"): The configuration target. Optional, defaults to
"workspace_folder".
Examples:
```lua
-- Update the value of "editor.tabSize"
vscode.update_config("editor.tabSize", 16, "global")
-- Update multiple configurations
vscode.update_config({ "editor.fontFamily", "editor.tabSize" }, { "Fira Code", 14 })
```
### vscode.notify(msg)
Show a vscode notification
You can set `vscode.notify` as your default notify function.
```lua
vim.notify = vscode.notify
```
### vscode.eval(code[, opts, timeout])
Evaluate javascript inside vscode and return the result. The code is executed in an async function context (so `await`
can be used). Use a `return` statement to return a value back to lua. Arguments passed from lua are available as the
`args` variable. The evaluated code has access to the
[VSCode API](https://code.visualstudio.com/api/references/vscode-api) through the `vscode` global.
Tips:
- Make sure to `await` on asynchronous functions when accessing the API.
- Use the global `logger` (e.g. `logger.info(...)`) to log messages to the output of vscode-neovim.
- JSON serializable values (primitives and simple objects) can be returned and will be automatically serialized then
deserialized to an equivalent lua value. If the return value is not JSON serializable then an error will be raised.
- `globalThis['some_name'] = ...` can be used to persist values between calls.
Parameters:
- `code` (string): The javascript to execute.
- `opts` (table): Map of optional parameters:
- `args` (any): a value to make available as the `args` variable in javascript. Can be a single value such as a
string or a table of multiple values.
- `timeout` (number): The number of milliseconds to wait for the evaluation to complete before cancelling. By default
there is no timeout.
Returns:
- The result of executing the provided code.
Examples:
```lua
local current_file = vscode.eval("return vscode.window.activeTextEditor.document.fileName")
local current_tab_is_pinned = vscode.eval("return vscode.window.tabGroups.activeTabGroup.activeTab.isPinned")
vscode.eval("await vscode.env.clipboard.writeText(args.text)", { args = { text = "some text" } })
```
### vscode.eval_async(code[, opts])
Like `vscode.eval()` but returns immediately and evaluates in the background instead.
Parameters:
- `code` (string): The javascript to execute.
- `opts` (table): Map of optional parameters:
- `args` (any): a value to make available as the `args` variable in javascript. Can be a single value such as a
string or a table of multiple values.
- `callback`: Function to handle the eval result. Must have this signature: `function(err: string|nil, ret: any)`:
- `err` is the error message, if any
- `ret` is the result
- If no callback is provided, error will be shown as a VSCode notification.
### vscode.with_insert(callback[, ms])
Perform operations in insert mode. If in visual mode, this function will **preserve the selection** after switching to
insert mode.
Parameters:
- `callback` (function): Callback function to run after switching to insert mode
- `ms` (number, optional): Milliseconds to defer the callback. Defaults to 30.
Example: make `editor.action.addSelectionToNextFindMatch` work correctly in any mode.
```lua
vim.keymap.set({ "n", "x", "i" }, "<C-d>", function()
vscode.with_insert(function()
vscode.action("editor.action.addSelectionToNextFindMatch")
end)
end)
```
![select-next](https://github.com/vscode-neovim/vscode-neovim/assets/47070852/5a93c87e-626a-4a70-a9ef-5084f747c7ef)
Example: make "editor.action.refactor" work correctly on the selection and support snippet manipulation after entering
VSCode snippet mode.
```lua
vim.keymap.set({ "n", "x" }, "<leader>r", function()
vscode.with_insert(function()
vscode.action("editor.action.refactor")
end)
end)
```
![refactor](https://github.com/vscode-neovim/vscode-neovim/assets/47070852/1c436b76-5c0b-42a7-8eb4-6f149761dd3c)
### Builtin module overrides
1. [`vim.ui`](https://github.com/asvetliakov/vscode-neovim/blob/HEAD/runtime/vscode/ui.lua): use VSCode's UI components.
2. [`vim.lsp.buf`](https://github.com/asvetliakov/vscode-neovim/blob/HEAD/runtime/vscode/lsp/buf.lua): execute corresponding VSCode LSP commands.
### VimScript
> **Note:** Since 1.0.0, vimscript functions are deprecated. Use the [Lua](#%EF%B8%8F-api) api instead.
- `VSCodeNotify()`/`VSCodeCall()`: deprecated, use [Lua](#%EF%B8%8F-api) `require('vscode').call()` instead.
- `VSCodeNotifyRange()`/`VSCodeCallRange()`: deprecated, use [Lua](#%EF%B8%8F-api)
`require('vscode').call(…, {range:…})` instead.
- `VSCodeNotifyRangePos()`/`VSCodeCallRangePos()`: deprecated, use [Lua](#%EF%B8%8F-api)
`require('vscode').call(…, {range:…})` instead.
You can also use `v:lua.require("vscode")` to access the API from VimScript.
## ⌨️ Keybindings (shortcuts)
There are three types of default/user keybindings:
- **Neovim keybindings**: These are the keybindings that are defined in the extension's vimscript files or the user's
`init.vim` file. These provide code navigation, buffer management, and other neovim-specific overrides.
- **VSCode keybindings**: These are the keybindings that are defined in the extension's `package.json` or the user's
`keybindings.json` file. These provide the ability to interact with VSCode's built-in features, and are used to make
VSCode more Vim-like.
- **VSCode passthrough keybindings**: These are the keybindings that are defined in the extension's `package.json` or
the user's `keybindings.json` file, but simply pass the keypress through to Neovim. These are used to allow Neovim to
handle certain keypresses that would otherwise be handled by VSCode.
This document only mentions some special cases, it is not an exhaustive list of keybindings and commands. Use VSCode and
Nvim features to see documentation and all defined shortcuts:
- Run the `Preferences: Open Keyboard Shortcuts` vscode command and search for "neovim" to see all vscode and
passthrough keybindings.
- Use the Nvim `:help` command to see the documentation for a given neovim command or keybinding. For example try
`:help :split` or `:help zo`.
- Note that `:help` for `<C-…>` bindings is spelled `CTRL-…`. For example to see the help for `<c-w>`, run
`:help CTRL-W`.
- Search the online Nvim documentation: <https://neovim.io/doc/user/>
- Reference the VSCode docs:
- Key Bindings: https://code.visualstudio.com/docs/getstarted/keybindings
- `"when"` clause: https://code.visualstudio.com/api/references/when-clause-contexts
### Keybinding Passthroughs
Every special (control/alt/non-alphanumerical) keyboard shortcut must be explicitly defined in VSCode to send to neovim.
By default, only bindings that are used by Neovim by default are sent.
> **Note:** if you want to pass additional control keys without adding a custom passthrough, see below.
To add a custom passthrough, for example <kbd>A-h</kbd> in normal mode, add to your `keybindings.json`:
```jsonc
{
"command": "vscode-neovim.send",
// Invoke the binding with this key sequence.
"key": "alt+h",
// Don't activate during insert mode.
// Docs for "when": https://code.visualstudio.com/api/references/when-clause-contexts
"when": "editorTextFocus && neovim.mode != insert",
// Send this input to Neovim.
"args": "<A-h>",
}
```
#### Insert mode control keys passthrough
Set by `vscode-neovim.ctrlKeysForInsertMode`.
Default: `["a", "d", "h", "j", "m", "o", "r", "t", "u", "w"]`
#### Normal mode control keys passthrough
Set by `ctrlKeysForNormalMode`.
Default:
`["a", "b", "d", "e", "f", "h", "i", "j", "k", "l", "m", "o", "r", "t", "u", "v", "w", "x", "y", "z", "/", "]"]`
#### Cmdline mode special keys passthrough
Always enabled.
- Tab, Up, Down
- Ctrl keys: `<C-h>` `<C-w>` `<C-u>` `<C-n>` `<C-p>` `<C-l>` `<C-g>` `<C-t>`
- All `<C-r>` prefixed keys
#### Disable passthrough for certain filetypes
Set by `editorLangIdExclusions`.
Disable keybindings defined by this extension in certain filetypes. Please note that this will not affect all
keybindings.
#### Remove other vscode or passthrough keybindings
If the above configuration flags do not provide enough control, you can remove the keybindings by editing your
`keybindings.json` or using the VSCode keybindings editor:
![remove keybindings](https://github.com/vscode-neovim/vscode-neovim/assets/47070852/816e9734-bac3-4bbb-ab7f-13bf2e364794)
### Code navigation bindings
| Key | VSCode Command |
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| <kbd>=</kbd> / <kbd>==</kbd> | `editor.action.formatSelection` |
| <kbd>gh</kbd> / <kbd>K</kbd> | `editor.action.showHover` |
| <kbd>gd</kbd> / <kbd>C-]</kbd> | `editor.action.revealDefinition` <br/> Also works in vim help. |
| <kbd>gf</kbd> | `editor.action.revealDeclaration` |
| <kbd>gH</kbd> | `editor.action.referenceSearch.trigger` |
| <kbd>gO</kbd> | `workbench.action.gotoSymbol` |
| <kbd>C-w</kbd> <kbd>gd</kbd> / <kbd>C-w</kbd> <kbd>gf</kbd> | `editor.action.revealDefinitionAside` |
| <kbd>gD</kbd> | `editor.action.peekDefinition` |
| <kbd>gF</kbd> | `editor.action.peekDeclaration` |
| <kbd>Tab</kbd> | `togglePeekWidgetFocus` <br/> Switch between peek editor and reference list. |
| <kbd>C-n</kbd> / <kbd>C-p</kbd> | Navigate lists, parameter hints, suggestions, quick-open, cmdline history, peek reference list |
> 💡 To specify the default peek mode, modify `editor.peekWidgetDefaultFocus` in your settings.
### Explorer/list navigation bindings
| Key | VSCode Command |
| ----------------------------------- | ------------------------------- |
| <kbd>j</kbd> or <kbd>k</kbd> | `list.focusDown/Up` |
| <kbd>h</kbd> or <kbd>l</kbd> | `list.collapse/select` |
| <kbd>Enter</kbd> | `list.select` |
| <kbd>gg</kbd> | `list.focusFirst` |
| <kbd>G</kbd> | `list.focusLast` |
| <kbd>o</kbd> | `list.toggleExpand` |
| <kbd>C-u</kbd> or <kbd>C-d</kbd> | `list.focusPageUp/Down` |
| <kbd>zo</kbd> or <kbd>zO</kbd> | `list.expand` |
| <kbd>zc</kbd> | `list.collapse` |
| <kbd>zC</kbd> | `list.collapseAllToFocus` |
| <kbd>za</kbd> or <kbd>zA</kbd> | `list.toggleExpand` |
| <kbd>zm</kbd> or <kbd>zM</kbd> | `list.collapseAll` |
| <kbd> / </kbd> or <kbd>Escape</kbd> | `list.toggleKeyboardNavigation` |
### Explorer file manipulation bindings
| Key | VSCode Command |
| ------------ | --------------------------------------------- |
| <kbd>r</kbd> | `renameFile` |
| <kbd>d</kbd> | `deleteFile` |
| <kbd>y</kbd> | `filesExplorer.copy` |
| <kbd>x</kbd> | `filesExplorer.cut` |
| <kbd>p</kbd> | `filesExplorer.paste` |
| <kbd>v</kbd> | `explorer.openToSide` |
| <kbd>a</kbd> | `explorer.newFile` |
| <kbd>A</kbd> | `explorer.newFolder` |
| <kbd>R</kbd> | `workbench.files.action.refreshFilesExplorer` |
### Hover widget manipulation bindings
| Key | VSCode Command |
| -------------- | -------------------------------- |
| <kbd>K</kbd> | `editor.action.showHover` |
| <kbd>h</kbd> | `editor.action.scrollLeftHover` |
| <kbd>j</kbd> | `editor.action.scrollDownHover` |
| <kbd>k</kbd> | `editor.action.scrollUpHover` |
| <kbd>l</kbd> | `editor.action.scrollRightHover` |
| <kbd>gg</kbd> | `editor.action.goToTopHover` |
| <kbd>G</kbd> | `editor.action.goToBottomHover` |
| <kbd>C-f</kbd> | `editor.action.pageDownHover` |
| <kbd>C-b</kbd> | `editor.action.pageUpHover` |
## 📟 Neovim Commands
Default commands and bindings are available for file/scroll/window/tab management.
#### File management
See [vscode-file-commands.vim](https://github.com/asvetliakov/vscode-neovim/blob/HEAD/runtime/vscode/overrides/vscode-file-commands.vim) for file commands reference.
The extension aliases various Nvim commands (`:edit`, `:enew`, `:find`, `:quit`, etc.) to equivalent vscode commands.
Also their normal-mode equivalents (where applicable) such as <kbd>C-w q</kbd>, etc.
#### Tab management
See [vscode-tab-commands.vim](https://github.com/asvetliakov/vscode-neovim/blob/HEAD/runtime/vscode/overrides/vscode-tab-commands.vim) for tab commands reference.
The extension aliases various Nvim tab commands (`:tabedit`, `:tabnew`, `:tabfind`, `:tabclose`, `:tabnext`,
`:tabprevious`, `:tabfirst`, `:tablast`) to equivalent vscode commands. Also their normal-mode equivalents (where
applicable) such as <kbd>gt</kbd>, etc.
#### Buffer/window management
See [vscode-window-commands.vim](https://github.com/asvetliakov/vscode-neovim/blob/HEAD/runtime/vscode/overrides/vscode-window-commands.vim) for file commands reference.
The extension aliases various Nvim buffer/window commands (`:split`, `:vsplit`, `:new`, `:vnew`, `:only`) to equivalent
vscode commands. Also their normal-mode equivalents (where applicable) such as <kbd>C-w s</kbd>, etc.
> 💡 Split size distribution is controlled by `workbench.editor.splitSizing` setting. By default, it's `distribute`,
> which is equal to vim's `equalalways` and `eadirection = 'both'` (default).
To use VSCode command 'Increase/decrease current view size' instead of separate bindings for width and height:
- `workbench.action.increaseViewSize`
- `workbench.action.decreaseViewSize`
<details>
<summary>Copy this into init.vim</summary>
```vim
function! s:manageEditorSize(...)
let count = a:1
let to = a:2
for i in range(1, count ? count : 1)
call VSCodeNotify(to ==# 'increase' ? 'workbench.action.increaseViewSize' : 'workbench.action.decreaseViewSize')
endfor
endfunction
" Sample keybindings. Note these override default keybindings mentioned above.
nnoremap <C-w>> <Cmd>call <SID>manageEditorSize(v:count, 'increase')<CR>
xnoremap <C-w>> <Cmd>call <SID>manageEditorSize(v:count, 'increase')<CR>
nnoremap <C-w>+ <Cmd>call <SID>manageEditorSize(v:count, 'increase')<CR>
xnoremap <C-w>+ <Cmd>call <SID>manageEditorSize(v:count, 'increase')<CR>
nnoremap <C-w>< <Cmd>call <SID>manageEditorSize(v:count, 'decrease')<CR>
xnoremap <C-w>< <Cmd>call <SID>manageEditorSize(v:count, 'decrease')<CR>
nnoremap <C-w>- <Cmd>call <SID>manageEditorSize(v:count, 'decrease')<CR>
xnoremap <C-w>- <Cmd>call <SID>manageEditorSize(v:count, 'decrease')<CR>
```
</details>
## 🎨 Highlights
There are two ways to customize highlight colors:
1. Set colors in nvim
**Note**: Due to the support for the `syntax` option requiring processing of syntax highlights, all built-in
highlight groups may be overridden or cleared. Therefore, please do not link any highlights to the built-in
highlight groups.
2. Set colors in vscode
- [vscode-neovim.highlightGroups.highlights](https://github.com/vscode-neovim/vscode-neovim/blob/2657c4506b3dffe0d069db2891e30cebd963c2be/package.json#L160C1-L202C19)
- [ThemeColor](https://code.visualstudio.com/api/references/theme-color)
## 🧰 Developing
Please see [CONTRIBUTING.md](https://github.com/asvetliakov/vscode-neovim/blob/HEAD/CONTRIBUTING.md) for details on how to contribute to this project.
## ❤️ Credits & External Resources
- [vim-altercmd](https://github.com/kana/vim-altercmd) - Used for rebinding default commands to call VSCode command.
- [neovim nodejs client](https://github.com/neovim/node-client) - NodeJS library for communicating with Neovim.
- [VSCodeVim](https://github.com/VSCodeVim/Vim) - Used for various inspiration.

View File

@@ -0,0 +1,46 @@
local api = require("vscode.api")
local default_optons = require("vscode.default-options")
local force_options = require("vscode.force-options")
local sync_options = require("vscode.sync-options")
local cursor = require("vscode.cursor")
local highlight = require("vscode.highlight")
local viewport = require("vscode.viewport")
default_optons.setup()
force_options.setup()
sync_options.setup()
cursor.setup()
highlight.setup()
viewport.setup()
local vscode = {
-- actions
action = api.action,
call = api.call,
eval = api.eval,
eval_async = api.eval_async,
-- hooks
on = api.on,
-- vscode settings
has_config = api.has_config,
get_config = api.get_config,
update_config = api.update_config,
-- notifications
notify = api.notify,
-- operatorfunc helper
to_op = api.to_op,
-- utilities
with_insert = api.with_insert,
}
-- Backward compatibility
package.loaded["vscode-neovim"] = vscode
return setmetatable(vscode, {
__index = function(_, key)
local msg = ([[The "vscode.%s" is missing. If you have a Lua module named "vscode", please rename it.]]):format(key)
vscode.notify(msg, vim.log.levels.ERROR)
return setmetatable({}, { __call = function() end })
end,
})

View File

@@ -0,0 +1,510 @@
local api = vim.api
local fn = vim.fn
local M = {}
------------------------
------- Requests -------
------------------------
local REQUEST_STATE = {
id = 0,
callbacks = {},
}
local function add_callback(callback)
REQUEST_STATE.id = REQUEST_STATE.id + 1
REQUEST_STATE.callbacks[REQUEST_STATE.id] = callback
return REQUEST_STATE.id
end
---Invoke the callback, called by vscode
---@param id number callback id
---@param result any result
---@param is_error boolean is error
function M.invoke_callback(id, result, is_error)
vim.schedule(function()
local callback = REQUEST_STATE.callbacks[id]
REQUEST_STATE.callbacks[id] = nil
if callback then
if is_error then
callback(result, nil)
else
callback(nil, result)
end
end
end)
end
---- Run an action asynchronously
---@param name string The action name, generally a vscode command
---@param opts? table Optional options table, all fields are optional
--- - args: (table) Optional arguments for the action
--- - range: (table) Specific range for the action. In visual mode, this parameter is generally not needed.
--- Three formats supported (All values are 0-indexed):
--- - [start_line, end_line]
--- - [start_line, start_character, end_line, end_character]
--- - {start = { line = start_line , character = start_character}, end = { line = end_line , character = end_character}}
--- - restore_selection: (boolean) Whether to preserve the current selection, only valid when `range` is specified. Defaults to `true`
--- - callback: (function(err: string|nil, ret: any))
--- Optional callback function to handle the action result.
--- The first argument is the error message, and the second is the result.
--- If no callback is provided, any error message will be shown as a notification in VSCode.
function M.action(name, opts)
opts = opts or {}
opts.restore_selection = opts.restore_selection ~= false
vim.validate({
name = { name, "string" },
opts = { opts, "table", true },
})
vim.validate({
["opts.callback"] = { opts.callback, "f", true },
["opts.args"] = { opts.args, "t", true },
["opts.range"] = {
opts.range,
function(range)
if range == nil then
return true
end
if type(range) ~= "table" then
return false
end
if vim.islist(opts.range) then
return #opts.range == 2 or #opts.range == 4
end
return range.start
and range.start.line
and range.start.character
and range["end"]
and range["end"].line
and range["end"].character
end,
},
["opts.restore_selection"] = { opts.restore_selection, "b", true },
})
if opts.args and not vim.islist(opts.args) then
opts.args = { opts.args }
end
if opts.callback then
opts.callback = add_callback(opts.callback)
end
vim.schedule(function()
vim.rpcnotify(vim.g.vscode_channel, "vscode-action", name, opts)
end)
end
--- Run an action synchronously
---@param name string The action name, generally a vscode command
---@param opts? table Optional options table, all fields are optional
--- - args: (table) Optional arguments for the action
--- - range: (table) Specific range for the action. In visual mode, this parameter is generally not needed.
--- Three formats supported (All values are 0-indexed):
--- - [start_line, end_line]
--- - [start_line, start_character, end_line, end_character]
--- - {start = { line = start_line , character = start_character}, end = { line = end_line , character = end_character}}
--- - restore_selection: (boolean) Whether to preserve the current selection, only valid when `range` is specified. Defaults to `true`
---@param timeout? number Timeout in milliseconds. The default value is -1, which means no timeout.
---
---@return any: result
function M.call(name, opts, timeout)
opts = opts or {}
opts.restore_selection = opts.restore_selection ~= false
timeout = timeout or -1
vim.validate({
name = { name, "string" },
opts = { opts, "table", true },
timeout = { timeout, "number", true },
})
vim.validate({
["opts.callback"] = { opts.callback, "nil" },
["opts.args"] = { opts.args, "t", true },
["opts.range"] = {
opts.range,
function(range)
if range == nil then
return true
end
if type(range) ~= "table" then
return false
end
if vim.islist(opts.range) then
return #opts.range == 2 or #opts.range == 4
end
return range.start
and range.start.line
and range.start.character
and range["end"]
and range["end"].line
and range["end"].character
end,
},
["opts.restore_selection"] = { opts.restore_selection, "b", true },
})
if opts.args and not vim.islist(opts.args) then
opts.args = { opts.args }
end
if timeout <= 0 then
return vim.rpcrequest(vim.g.vscode_channel, "vscode-action", name, opts)
end
local done = false
local err, res
opts.callback = function(_err, _res)
err = _err
res = _res
done = true
end
M.action(name, opts)
vim.wait(timeout, function()
return done
end)
if done then
if err == nil then
return res
else
error(err)
end
else
error(string.format("Call '%s' timed out.", name))
end
end
--- Evaluate javascript synchronously inside vscode with access to the
--- [VSCode API](https://code.visualstudio.com/api/references/vscode-api) and return the result.
---
---@param code string the javascript code to run
--- - the code runs in an async function context
--- (so `await` can be used. Make sure to `await` if calling an async function from the VSCode API)
--- - use `return` to return a value to lua
--- - use the `vscode` variable to access the VSCode API
--- - use the `args` variable to access any arguments passed from lua
---@param opts? table Optional options table, all fields are optional
--- - args: (any) Optional arguments to serialize and make available to the code being run (as the `args` variable)
---@param timeout? number Timeout in milliseconds. The default value is -1, which means no timeout.
---
---@return any: the result of evaluating the given code in VSCode
function M.eval(code, opts, timeout)
vim.validate({
code = { code, "string" },
opts = { opts, "table", true },
timeout = { timeout, "number", true },
})
opts = opts or {}
opts.args = { code, opts.args }
return M.call("eval", opts, timeout)
end
--- Evaluate javascript asynchronously inside vscode with access to the
--- [VSCode API](https://code.visualstudio.com/api/references/vscode-api).
---
---@param code string the javascript code to run
---@param opts? table Optional options table, all fields are optional
--- - args: (any) Optional arguments to serialize and make available to the code being run (as the `args` variable)
--- - callback: (function(err: string|nil, ret: any))
--- Optional callback function to handle the evaluated result.
--- The first argument is the error message, and the second is the result.
--- If no callback is provided, any error message will be shown as a notification in VSCode.
function M.eval_async(code, opts)
vim.validate({
code = { code, "string" },
opts = { opts, "table", true },
})
opts = opts or {}
opts.args = { code, opts.args }
M.action("eval", opts)
end
---------------------------
------- Event Hooks -------
---------------------------
local EVENT_STATE = {}
--[[
List of events:
event -> args
-------------
]]
---@param event string
---@param callback function
function M.on(event, callback)
vim.validate({
event = { event, "string" },
callback = { callback, "function" },
})
local cbs = EVENT_STATE[event] or {}
if not vim.tbl_contains(cbs, callback) then
table.insert(cbs, callback)
EVENT_STATE[event] = cbs
end
end
---@param event string
---@vararg any
function M.fire_event(event, ...)
local args = { ... }
vim.schedule(function()
vim.tbl_map(function(cb)
cb(unpack(args))
end, EVENT_STATE[event] or {})
end)
end
-------------------------------------------
------- VSCode settings integration -------
-------------------------------------------
---Check if configuration has a certain value.
---@param name string|string[] The configuration name or an array of configuration names.
---@return boolean|boolean[] Returns true if the configuration has a certain value, false otherwise.
--- If name is an array, returns an array of booleans indicating whether each configuration
--- has a certain value or not.
function M.has_config(name)
vim.validate({ name = { name, { "s", "t" } } })
return M.call("has_config", { args = { name } })
end
---Get configuration value
---@param name string|string[] The configuration name or an array of configuration names.
---@return unknown|unknown[] The value of the configuration. If name is an array,
--- returns an array of values corresponding to each configuration.
function M.get_config(name)
vim.validate({ name = { name, { "s", "t" } } })
return M.call("get_config", { args = { name } })
end
---Update configuration value
---@param name string|string[] The configuration name or an array of configuration names.
---@param value unknown|unknown[] The new value for the configuration.
---@param target nil|"global"|"workspace"|"workspace_folder" The configuration target. Defaults to "workspace_folder".
function M.update_config(name, value, target)
vim.validate({ name = { name, { "s", "t" } } })
local name_is_table = type(name) == "table"
local value_is_table = type(value) == "table"
if name_is_table and not value_is_table then
error([[The "name" is a table, but the "value" is not]])
elseif value_is_table and not name_is_table then
error([[The "value" is a table, but the "name" is not]])
end
assert(
target == nil or target == "global" or target == "workspace" or target == "workspace_folder",
[[The "target" must be nil or one of "global", "workspace", or "workspace_folder"]]
)
return M.call("update_config", { args = { name, value, target } })
end
---------------------------
------ Notifications ------
---------------------------
--- Display a notification to the user.
---
---@param msg string Content of the notification to show to the user.
---@param level integer|nil One of the values from |vim.log.levels|.
---@param opts table|nil Optional parameters. Unused by default.
---@diagnostic disable-next-line: unused-local
function M.notify(msg, level, opts)
local levels = vim.log.levels
level = level or levels.INFO
-- legacy
if type(level) == "string" then
if level == "error" then
level = levels.ERROR
elseif level == "warn" then
level = levels.WARN
else
level = levels.INFO
end
end
local cmd
if level >= levels.ERROR then
cmd = "await vscode.window.showErrorMessage(args)"
elseif level >= levels.WARN then
cmd = "await vscode.window.showWarningMessage(args)"
else
cmd = "await vscode.window.showInformationMessage(args)"
end
M.eval_async(cmd, { args = msg })
end
---------------------------------
------ map-operator helper ------
---------------------------------
do
---@class Context
---@field range lsp.Range
---@field is_linewise boolean true indicates linewise, otherwise it is charwise.
---@field is_single_line boolean true if start.line and end.line are equal.
---@field is_current_line boolean is single line, and is current line
local op_func_id = 0
---@see map-operator
---
---Example: Remap 'gq' to use 'editor.action.formatSelection'
---
---```lua
--- local format = vscode.to_op(function(ctx)
--- vscode.action("editor.action.formatSelection", { range = ctx.range })
--- end)
---
--- vim.keymap.set({ "n", "x" }, "gq", format, { expr = true })
--- vim.keymap.set({ "n" }, "gqq", function()
--- return format() .. "_"
--- end, { expr = true })
---````
function M.to_op(func)
op_func_id = op_func_id + 1
local op_func_name = "__vscode_op_func_" .. tostring(op_func_id)
local operatorfunc = "v:lua." .. op_func_name
local op_func = function(motion)
local mode = api.nvim_get_mode().mode
if not motion then
if mode == "n" then
vim.go.operatorfunc = operatorfunc
return "g@"
elseif mode ~= "\x16" and mode:lower() ~= "v" then
return "<Ignore>"
end
end
local start_pos
local end_pos
if motion then
start_pos = api.nvim_buf_get_mark(0, "[")
end_pos = api.nvim_buf_get_mark(0, "]")
else
local a = fn.getpos("v")
local b = fn.getpos(".")
start_pos = { a[2], a[3] - 1 }
end_pos = { b[2], b[3] - 1 }
end
if start_pos[1] > end_pos[1] or (start_pos[1] == end_pos[1] and start_pos[2] > end_pos[2]) then
start_pos, end_pos = end_pos, start_pos
end
local is_linewise = motion == "line" or mode == "V"
if is_linewise then
start_pos = { start_pos[1], 0 }
end_pos = { end_pos[1], #fn.getline(end_pos[1]) }
end
local range = vim.lsp.util.make_given_range_params(start_pos, end_pos, 0, "utf-16").range
local is_single_line = range.start.line == range["end"].line
local is_current_line = is_single_line and range.start.line == fn.line(".") - 1
---@type Context
local ctx = {
range = range,
is_linewise = is_linewise,
is_single_line = is_single_line,
is_current_line = is_current_line,
}
func(ctx)
return "<Ignore>"
end
_G[op_func_name] = op_func
return _G[op_func_name]
end
end
-----------------------
------ utilities ------
-----------------------
---Perform operations in insert mode
---If in visual mode, this function will preserve the selection after
---switching to insert mode.
---
---Example:
---Make `editor.action.addSelectionToNextFindMatch` work correctly in any mode.
---This is the behavior of the default VSCode shortcut Ctrl+d: Add Selection To Next Find Match.
---
---```lua
---vim.keymap.set({ "n", "x", "i" }, "<C-d>", function()
--- vscode.with_insert(function()
--- vscode.action("editor.action.addSelectionToNextFindMatch")
--- end)
---end)
---````
---
---@param callback function Callback function to run after switching to insert mode.
---@param ms? number Milliseconds to defer the callback. Defaults to 30.
function M.with_insert(callback, ms)
vim.validate({ callback = { callback, "f" }, ms = { ms, "n", true } })
local mode = api.nvim_get_mode().mode
local startinsert = function(keys)
keys = api.nvim_replace_termcodes(keys, true, true, true)
api.nvim_feedkeys(keys, "n", false)
end
---@param ranges lsp.Range[]|nil`
local run_callback = function(ranges)
if ranges then
M.action("start-multiple-cursors", { args = { ranges }, callback = callback })
else
M.action("noop", { callback = callback })
end
end
--- Insert ---
if mode == "i" then
run_callback()
return
end
--- Normal ---
if mode == "n" then
startinsert("i")
run_callback()
return
end
--- Visual ---
if mode:match("[vV\x16]") then
local A = fn.getpos("v")
local B = fn.getpos(".")
local start_pos = { A[2], A[3] - 1 }
local end_pos = { B[2], B[3] - 1 }
if start_pos[1] > end_pos[1] or (start_pos[1] == end_pos[1] and start_pos[2] > end_pos[2]) then
start_pos, end_pos = end_pos, start_pos
end
if mode == "V" then
start_pos = { start_pos[1], 0 }
end_pos = { end_pos[1], #fn.getline(end_pos[1]) }
end
local range = vim.lsp.util.make_given_range_params(start_pos, end_pos, 0, "utf-16").range
local ranges = { range }
api.nvim_win_set_cursor(0, end_pos)
startinsert("<Esc>" .. (vim.o.selection ~= "exclusive" and "a" or "i"))
-- Wait for the cursor synchronization task to complete in VSCode
vim.defer_fn(function()
run_callback(ranges)
end, ms or 30)
return
end
--- Other ---
startinsert("<Esc><Esc>i")
run_callback()
end
return M

View File

@@ -0,0 +1,161 @@
local api, fn = vim.api, vim.fn
local vscode = require("vscode.api")
local util = require("vscode.util")
-- this module is responsible for creating multiple cursors, triggering a visual update, and displaying the fake visual cursor
local M = {}
-- ------------------------------ multi cursor ------------------------------ --
local multi_cursor_task
local function start_multi_cursor(right, skip_empty)
multi_cursor_task = nil
local mode = api.nvim_get_mode().mode
local is_line = mode == "V"
local is_block = mode == "\x16"
if not is_line and not is_block then
return
end
api.nvim_feedkeys(api.nvim_replace_termcodes("<ESC>" .. (right and "a" or "i"), true, true, true), "n", true)
multi_cursor_task = function()
multi_cursor_task = nil
local ranges = {} ---@type lsp.Range[]
local start_pos = api.nvim_buf_get_mark(0, "<") ---@type number[]
local end_pos = api.nvim_buf_get_mark(0, ">") ---@type number[]
for row = start_pos[1], end_pos[1] do
local line = vim.fn.getline(row)
if #line == 0 and (skip_empty or is_block) then
else
-- (row, col) is (1, 0)-indexed
local s_col, e_col
if is_line then
s_col = #(line:match("^%s*") or "")
e_col = #line
else
e_col = math.min(#line, end_pos[2])
s_col = math.min(e_col, start_pos[2])
end
local range = vim.lsp.util.make_given_range_params({ row, s_col }, { row, e_col }, 0, "utf-16").range
if right then
range = { start = range["end"], ["end"] = range["end"] }
else
range = { start = range.start, ["end"] = range.start }
end
table.insert(ranges, range)
end
end
if #ranges > 0 then
vscode.action("start-multiple-cursors", { args = { ranges } })
end
end
end
function M.setup_multi_cursor(group)
vim.api.nvim_create_autocmd({ "InsertEnter" }, {
group = group,
callback = function()
if multi_cursor_task then
vim.schedule(multi_cursor_task)
end
end,
})
-- Multiple cursors support for visual line/block modes
vim.keymap.set("x", "ma", function()
start_multi_cursor(true, true)
end)
vim.keymap.set("x", "mi", function()
start_multi_cursor(false, true)
end)
vim.keymap.set("x", "mA", function()
start_multi_cursor(true, false)
end)
vim.keymap.set("x", "mI", function()
start_multi_cursor(false, false)
end)
end
-- ----------------------- forced visual cursor updates ----------------------- --
function M.setup_visual_changed(group)
-- Simulate VisualChanged event
-- TODO: https://github.com/neovim/neovim/issues/19708
local visual_ns = api.nvim_create_namespace("vscode.visual.changed")
local is_visual, last_visual_pos, last_curr_pos
local function fire_visual_changed()
fn.VSCodeExtensionNotify("visual-changed", api.nvim_get_current_win())
end
api.nvim_create_autocmd({ "ModeChanged" }, {
group = group,
callback = function(ev)
local mode = api.nvim_get_mode().mode
-- save current mode
is_visual = mode:match("[vV\x16]")
-- handle mode changes
if ev.match:match("[vV\x16]") then
last_visual_pos = fn.getpos("v")
last_curr_pos = fn.getpos(".")
fire_visual_changed()
end
end,
})
api.nvim_set_decoration_provider(visual_ns, {
on_win = function()
if is_visual then
local visual_pos = fn.getpos("v")
local curr_pos = fn.getpos(".")
if not (vim.deep_equal(visual_pos, last_visual_pos) and vim.deep_equal(curr_pos, last_curr_pos)) then
last_visual_pos = visual_pos
last_curr_pos = curr_pos
fire_visual_changed()
end
end
end,
})
end
-- --------------------------- fake visual cursor --------------------------- --
-- in visual mode, decorate a fake cursor so that vscode can use the primary cursor for selection
local fake_cursor_ns = api.nvim_create_namespace("vscode.fake-visual-cursor")
local fake_cursor = nil
local function highlight_fake_cursor()
if fake_cursor then
fake_cursor = nil
for _, buf in ipairs(api.nvim_list_bufs()) do
api.nvim_buf_clear_namespace(buf, fake_cursor_ns, 0, -1)
end
end
if util.is_visual_mode() then
local line = vim.fn.line(".")
local col = vim.fn.col(".")
local ch = util.get_char_at(line, col) or " "
fake_cursor = api.nvim_buf_set_extmark(0, fake_cursor_ns, line - 1, col - 1, {
virt_text = { { ch, "Cursor" } },
virt_text_pos = "overlay",
priority = 65534,
})
end
end
function M.setup_fake_cursor(group)
api.nvim_create_autocmd({ "ModeChanged", "CursorMoved" }, {
group = group,
callback = highlight_fake_cursor,
})
end
-- ------------------------------ setup ------------------------------ --
function M.setup()
local group = vim.api.nvim_create_augroup("vscode.cursor", { clear = true })
M.setup_multi_cursor(group)
M.setup_visual_changed(group)
M.setup_fake_cursor(group)
end
return M

View File

@@ -0,0 +1,16 @@
local function setup()
vim.cmd.syntax("on")
-- customise statusbar
vim.opt.shortmess = "filnxtToOFI"
-- disable matchparen because we don't need it
vim.g.loaded_matchparen = 1
-- When enable `ext_messages`, `cmdheight` will be set to 0 by default.
vim.opt.cmdheight = 2
-- Change the default statusline to an empty string
vim.opt.statusline = "%##"
end
return { setup = setup }

View File

@@ -0,0 +1,69 @@
--- This file is used to force set options which may break the extension.
local M = {}
-- --------------------- forced global and local critical options -------------------- --
local function forceoptions(opt)
opt.wrap = false
opt.conceallevel = 0
opt.hidden = true
opt.bufhidden = "hide"
opt.list = true
-- Fix the gutter width, no need to consider highlighting issues caused by number, signcolumn, foldcolumn anymore.
-- {{
opt.numberwidth = 1
opt.statuscolumn = "%#NonText#" .. ("-"):rep(20) -- max-signcolumn(9) + max-foldcolumn(9) + numberwidth(1) + 1
-- }}
opt.listchars = { tab = " " }
--- Turn off auto-folding
opt.foldenable = false
opt.foldcolumn = "0"
opt.foldmethod = "manual"
--- lazyredraw breaks the movement
opt.lazyredraw = false
end
local function force_global_options()
vim.opt.wildmode = "longest:full,full"
vim.cmd([[set wildchar=<Tab>]])
vim.opt.mouse = "a"
vim.opt.backup = false
vim.opt.wb = false
vim.opt.swapfile = false
vim.opt.autoread = false
vim.opt.autowrite = false
vim.opt.cursorline = false
vim.opt.signcolumn = "no"
vim.opt.winblend = 0
vim.opt.ruler = false
vim.opt.colorcolumn = nil
if vim.fn.exists("&winborder") == 1 then
vim.opt.winborder = ""
end
forceoptions(vim.opt)
end
local function force_local_options()
forceoptions(vim.opt_local)
end
function M.setup()
-- force options on startup
force_global_options()
force_local_options()
local group = vim.api.nvim_create_augroup("vscode.force-options", { clear = true })
vim.api.nvim_create_autocmd({ "VimEnter" }, {
group = group,
callback = force_global_options,
})
vim.api.nvim_create_autocmd({ "VimEnter", "BufEnter", "FileType" }, {
group = group,
callback = force_local_options,
})
end
return M

View File

@@ -0,0 +1,193 @@
---@diagnostic disable: inject-field
-- Copy global highlights and overrides highlights to the custom namespace, only external buffers use global namespace
local api = vim.api
local vscode = require("vscode.api")
local NS = api.nvim_create_namespace("vscode.highlight")
vim.opt.conceallevel = 0
vim.g.html_ignore_conceal = 1
vim.g.vim_json_conceal = 0
vim.g.markdown_recommended_style = 0
vim.g.markdown_folding = 0
---Link highlights with same values to the same highlight, to avoid performance
---and rendering issues with vscode decorations caused by a large number of
---highlight IDs due to highlight merging.
---Currently, only handle highlights that need to be cleared
api.nvim_set_hl(0, "VSCodeNone", {})
---@param id number
---@param name string
---@param value table
local function set_hl(id, name, value)
if vim.tbl_isempty(value) then
return api.nvim_set_hl(id, name, { link = "VSCodeNone" })
end
return api.nvim_set_hl(id, name, value)
end
local function setup_globals()
local custom_hls = vscode.get_config("vscode-neovim.highlightGroups.highlights")
if type(custom_hls) ~= "table" then
custom_hls = {}
end
for hl in pairs(custom_hls) do
-- If we directly clear the highlighting, it may cause the highlighting to
-- become "unavailable". For example, nvim_buf_set_extmark will ignore the
-- highlighting that has no visual effect on the screen.
-- So we use this special and useless attribute to allow highlighting to
-- trigger rendering normally.
-- In vscode, we will remove this attribute so that we can use the empty
-- attribute to determine whether to use custom highlighting. This allows
-- other highlights to be rendered correctly when mixed with custom
-- highlighting.
set_hl(0, hl, { altfont = true })
end
-- stylua: ignore start
local hls = {
Normal = {},
NormalNC = {},
NormalFloat = {},
Visual = {},
VisualNC = {},
VisualNOS = {},
Substitute = {},
Whitespace = {},
LineNr = {},
LineNrAbove = {},
LineNrBelow = {},
CursorLine = {},
CursorLineNr = {},
ColorColumn = {},
FoldColumn = {},
Folded = {},
Sign = {},
SignColumn = {},
MsgSeparator = {},
MsgArea = {},
Question = {},
QuickFixLine = {},
EndOfBuffer = {},
Debug = {},
MatchParen = {},
CursorColumn = {},
NonText = {},
-- make cursor visible for plugins that use fake cursor
Cursor = { reverse = true },
}
-- stylua: ignore end
for name, attrs in pairs(hls) do
if not custom_hls[name] then
set_hl(0, name, attrs)
end
end
end
-- stylua: ignore start
local overrides = {
Delimiter = {}, Identifier = {}, SpecialChar = {}, Number = {}, Type = {},
String = {}, Error = {}, Comment = {}, Constant = {}, Special = {},
Statement = {}, PreProc = {}, Underlined = {}, Ignore = {}, Todo = {},
Character = {}, Boolean = {}, Float = {}, Function = {}, Conditional = {},
Repeat = {}, Label = {}, Keyword = {}, Exception = {}, Include = {},
Define = {}, Macro = {}, PreCondit = {}, StorageClass = {}, Structure = {},
Typedef = {}, Tag = {}, SpecialComment = {}, Operator = {}, Debug = {},
}
-- stylua: ignore end
local overridden = {}
local function setup_syntax_overrides()
for name, attrs in pairs(overrides) do
if not overridden[name] then
overridden[name] = true
set_hl(NS, name, attrs)
end
end
end
local cleared_syntax_groups = {}
local function setup_syntax_groups()
local output = api.nvim_exec2("syntax", { output = true })
local items = vim.split(output.output, "\n")
for _, item in ipairs(items) do
local group = item:match([[([%w@_%.]+)%s+xxx]])
if group and not cleared_syntax_groups[group] then
cleared_syntax_groups[group] = true
set_hl(NS, group, {})
end
end
end
local function setup_win_hl_ns()
local ok, curr_ns, target_ns, vscode_controlled
for _, win in ipairs(api.nvim_list_wins()) do
local buf = api.nvim_win_get_buf(win)
ok, curr_ns = pcall(api.nvim_win_get_var, win, "_vscode_hl_ns")
curr_ns = ok and curr_ns or 0
ok, vscode_controlled = pcall(api.nvim_buf_get_var, buf, "vscode_controlled")
target_ns = (ok and vscode_controlled) and NS or 0
if curr_ns ~= target_ns then
api.nvim_win_set_var(win, "_vscode_hl_ns", target_ns)
api.nvim_win_set_hl_ns(win, target_ns)
end
end
end
local function disable_treesitter_highlights()
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
pcall(function()
-- Hack to disable treesitter highlighting by monkey patching
-- `for_each_highlight_state()`. Could use `vim.treesitter.stop()`, but
-- this seems to have unintended side-effects as per
-- https://github.com/vscode-neovim/vscode-neovim/issues/2355
local highlighter = vim.treesitter.highlighter.active[buf]
---@diagnostic disable-next-line: duplicate-set-field
function highlighter:for_each_highlight_state() end
end)
end
end
local function setup()
local group = api.nvim_create_augroup("vscode.highlight", { clear = true })
-- WinScrolled is perhaps not strictly necessary (or shouldn't be), but is useful in ensuring
-- we don't get syntax highlights in peek windows.
api.nvim_create_autocmd({ "WinNew", "WinEnter", "WinScrolled", "BufNew", "BufEnter", "BufWinEnter" }, {
group = group,
callback = function()
setup_win_hl_ns()
disable_treesitter_highlights()
end,
})
api.nvim_create_autocmd({ "ColorScheme", "Syntax", "FileType" }, {
group = group,
callback = function(ev)
api.nvim_set_hl(0, "VSCodeNone", {})
if ev.event == "ColorScheme" then
setup_globals()
-- highlights of custom namespace
setup_syntax_overrides()
end
setup_syntax_groups()
-- wait syntax things done
vim.defer_fn(setup_syntax_groups, 200)
end,
})
api.nvim_create_autocmd({ "VimEnter", "UIEnter" }, {
group = group,
callback = function()
disable_treesitter_highlights()
setup_globals()
setup_syntax_groups()
setup_syntax_overrides()
setup_win_hl_ns()
end,
})
end
return { setup = setup }

View File

@@ -0,0 +1,379 @@
---@diagnostic disable: deprecated
local api, fn = vim.api, vim.fn
local vscode = require("vscode.api")
local util = require("vscode.util")
local M = {}
---call from vscode to sync viewport with neovim
---@param vscode_topline number the top line of vscode visible range
---@param vscode_endline number the end line of vscode visible range
function M.scroll_viewport(vscode_topline, vscode_endline)
local current_height = vim.api.nvim_win_get_height(0)
local new_height = vscode_endline - vscode_topline + 1
-- resize height
if current_height ~= new_height then
vim.api.nvim_win_set_height(0, new_height)
end
local top_line = vim.fn.line("w0")
local diff = top_line - vscode_topline
if diff ~= 0 and (vscode_topline > 0) then
vim.fn.winrestview({
topline = vscode_topline,
})
end
end
---@class CleanupOpts
---@field windows number[]
---@field buffers number[]
---Close windows and buffers. This is done together in one call to reduce RPC
---overhead, but still ensures buffer cleanup happens after window cleanup.
---@param opts CleanupOpts
function M.cleanup_windows_and_buffers(opts)
for _, win in ipairs(opts.windows) do
pcall(vim.api.nvim_win_close, win, true)
end
for _, buf in ipairs(opts.buffers) do
pcall(vim.api.nvim_buf_delete, buf, { force = true })
end
end
---Handle document changes
---@param bufnr number
---@param changes (string | integer)[][]
---@return number: changed tick of the buffer
function M.handle_changes(bufnr, changes)
-- Save and restore local marks
-- Code modified from https://github.com/neovim/neovim/pull/14630
local marks = {}
for _, m in pairs(fn.getmarklist(bufnr or api.nvim_get_current_buf())) do
if m.mark:match("^'[a-z]$") then
marks[m.mark:sub(2, 2)] = { m.pos[2], m.pos[3] - 1 } -- api-indexed
end
end
-- The changes from vscode are all expected and must be applied.
local ro = vim.bo[bufnr].ro
local ma = vim.bo[bufnr].ma
vim.bo[bufnr].ro = false
vim.bo[bufnr].ma = true
for _, change in ipairs(changes) do
api.nvim_buf_set_text(bufnr, unpack(change))
end
vim.bo[bufnr].ro = ro
vim.bo[bufnr].ma = ma
local max = api.nvim_buf_line_count(bufnr)
-- no need to restore marks that still exist
for _, m in pairs(fn.getmarklist(bufnr or api.nvim_get_current_buf())) do
marks[m.mark:sub(2, 2)] = nil
end
-- restore marks
for mark, pos in pairs(marks) do
if pos then
-- make sure we don't go out of bounds
local line = (api.nvim_buf_get_lines(bufnr, pos[1] - 1, pos[1], false))[1] or ""
pos[1] = math.min(pos[1], max)
pos[2] = math.min(pos[2], #line)
api.nvim_buf_set_mark(bufnr or 0, mark, pos[1], pos[2], {})
end
end
return api.nvim_buf_get_changedtick(bufnr)
end
do
--- Replay changes for dotrepeat ---
local _curr_win, _temp_buf, _temp_win
---@param edits string
---@param deletes number
function M.dotrepeat_sync(edits, deletes)
local ei = vim.opt.ei:get()
vim.opt.ei = "all"
_curr_win = api.nvim_get_current_win()
_temp_buf = api.nvim_create_buf(false, true)
_temp_win = api.nvim_open_win(_temp_buf, true, { external = true, width = 100, height = 50 })
if deletes > 0 then
api.nvim_buf_set_lines(_temp_buf, 0, -1, false, { ("x"):rep(deletes) })
api.nvim_win_set_cursor(_temp_win, { 1, deletes })
local bs = ("<BS>"):rep(deletes)
bs = api.nvim_replace_termcodes(bs, true, true, true)
api.nvim_feedkeys(bs, "n", false)
end
api.nvim_feedkeys(edits, "n", true)
vim.opt.ei = ei
end
function M.dotrepeat_restore()
local ei = vim.opt.ei:get()
vim.opt.ei = "all"
api.nvim_set_current_win(_curr_win)
pcall(api.nvim_win_close, _temp_win, true)
pcall(api.nvim_buf_delete, _temp_buf, { force = true })
vim.opt.ei = ei
end
end
---Get editor's selections
---@return lsp.Range[]
function M.get_selections(win)
win = win or api.nvim_get_current_win()
local buf = api.nvim_win_get_buf(win)
local mode = api.nvim_get_mode().mode
local is_visual = mode:match("[vV\x16]")
local function wincall(cb)
return api.nvim_win_call(win, cb)
end
-- normal
if not is_visual then
local pos = vim.lsp.util.make_position_params(win, "utf-16").position
return { { start = pos, ["end"] = pos } }
end
-- linewise/charwise visual
if mode:lower() == "v" then
local start_pos, end_pos
wincall(function()
start_pos = { fn.line("v"), fn.col("v") - 1 }
end_pos = { fn.line("."), fn.col(".") - 1 }
end)
local start_from_left = true
if start_pos[1] > end_pos[1] or (start_pos[1] == end_pos[1] and start_pos[2] > end_pos[2]) then
start_from_left = false
start_pos, end_pos = end_pos, start_pos
end
if mode == "V" then
start_pos = { start_pos[1], 0 }
end_pos = { end_pos[1], #(fn.getbufline(buf, end_pos[1])[1] or "") }
end
local range = vim.lsp.util.make_given_range_params(start_pos, end_pos, buf, "utf-16").range
if not start_from_left then
range = { start = range["end"], ["end"] = range.start }
end
return { range }
end
-- blockwise visual
local ranges = {}
-- 1-indexed {
local start_line_1, end_line_1, start_vcol, end_vcol
wincall(function()
start_line_1 = fn.line("v")
end_line_1 = fn.line(".")
start_vcol = fn.virtcol("v")
end_vcol = fn.virtcol(".")
end)
local curr_line_1 = end_line_1
-- }
local top_to_bottom = start_line_1 < end_line_1 or (start_line_1 == end_line_1 and start_vcol <= end_vcol)
local start_from_left = end_vcol >= start_vcol
if start_line_1 > end_line_1 then
start_line_1, end_line_1 = end_line_1, start_line_1
end
if start_vcol > end_vcol then
start_vcol, end_vcol = end_vcol, start_vcol
end
for line_1 = start_line_1, end_line_1 do
local line_0 = line_1 - 1
local line_text = fn.getbufline(buf, line_1)[1] or ""
local line_diswidth = wincall(function()
return fn.strdisplaywidth(line_text)
end)
if start_vcol > line_diswidth then
if line_1 == curr_line_1 then
local pos = { line = line_0, character = ({ vim.str_utfindex(line_text) })[2] }
table.insert(ranges, { start = pos, ["end"] = pos })
else
-- ignore
end
else
local start_col = util.virtcol2col(win, line_1, start_vcol)
local end_col = util.virtcol2col(win, line_1, end_vcol)
local start_col_offset = fn.strlen(util.get_char_at(line_1, start_col, buf) or "")
local end_col_offset = fn.strlen(util.get_char_at(line_1, end_col, buf) or "")
local range = vim.lsp.util.make_given_range_params(
{ line_1, math.max(0, start_col - start_col_offset) },
{ line_1, math.max(0, end_col - end_col_offset) },
buf,
"utf-16"
).range
if not start_from_left then
range = { start = range["end"], ["end"] = range.start }
end
table.insert(ranges, range)
end
end
if #ranges == 0 then
-- impossible
local pos = vim.lsp.util.make_position_params(win, "utf-16").position
return { { start = pos, ["end"] = pos } }
end
if top_to_bottom then
local ret = {}
for i = #ranges, 1, -1 do
table.insert(ret, ranges[i])
end
return ret
else
return ranges
end
end
---@param buf integer
---@param anchor lsp.Position
---@param active lsp.Position
function M.start_visual(buf, anchor, active)
if buf ~= api.nvim_get_current_buf() then
return
end
if util.compare_position(anchor, active) == 1 then
anchor.character = math.max(0, anchor.character - 1)
else
active.character = math.max(0, active.character - 1)
end
local anchor_line = anchor.line + 1
local active_line = active.line + 1
local anchor_line_text = util.get_line(buf, anchor.line)
local active_line_text = util.get_line(buf, active.line)
local anchor_col = vim.str_byteindex(anchor_line_text, anchor.character, true)
local active_col = vim.str_byteindex(active_line_text, active.character, true)
local v = fn.visualmode(1)
api.nvim_buf_set_mark(buf, "<", anchor_line, anchor_col, {})
api.nvim_buf_set_mark(buf, ">", active_line, active_col, {})
api.nvim_feedkeys((v == "V" or v == "\x16") and "gvv" or "gv", "n", false)
end
---Translate from a Windows path to a WSL path
---@param path string
---@return string
function M.wslpath(path)
local ok, ret = pcall(vim.fn.system, { "wslpath", path })
if not ok then
vim.notify(ret, vim.log.levels.ERROR)
return path
end
return vim.trim(ret)
end
--#region Buffer management
--- 1. Implements :write and related commands, via buftype=acwrite. #521 #1260
--- 2. Syncs buffer modified status with vscode. #247
local function set_buffer_autocmd(buf)
api.nvim_create_autocmd({ "BufWriteCmd" }, {
buffer = buf,
callback = function(ev)
local current_name = api.nvim_buf_get_name(ev.buf)
local target_name = ev.match
local data = {
buf = ev.buf,
bang = vim.v.cmdbang == 1,
current_name = current_name,
target_name = target_name,
}
vscode.action("save_buffer", { args = { data } })
end,
})
api.nvim_create_autocmd({ "BufModifiedSet" }, {
buffer = buf,
callback = function(ev)
fn.VSCodeExtensionNotify("BufModifiedSet", {
buf = ev.buf,
modified = vim.bo[ev.buf].mod,
})
end,
})
end
---@class InitDocumentBufferData
---@field buf number
---@field lines string[]
---@field editor_options EditorOptions
---@field uri string
---@field uri_data table
---@field modifiable boolean
---@field bufname string
---@field modified boolean
---@field filetype string|vim.NIL
---@param data InitDocumentBufferData
function M.init_document_buffer(data)
local buf = data.buf
-- 1. Force filetype before setting buffer name and lines, vim.filetype will handle the b:vscode_filetype
-- 2. Finally, set the filetype again just in case
local force_filetype = function()
if data.filetype and data.filetype ~= vim.NIL then
api.nvim_buf_set_var(buf, "vscode_filetype", data.filetype)
api.nvim_buf_set_option(buf, "filetype", data.filetype)
end
end
force_filetype()
-- Set bufname before setting lines so that filetype detection can work ???
api.nvim_buf_set_name(buf, data.bufname)
-- Let nvim resolve the physical path of our file to avoid relative path issues
-- with symbolic links when saving the buffer. #2284
api.nvim_buf_set_name(buf, api.nvim_buf_get_name(buf))
api.nvim_buf_set_lines(buf, 0, -1, false, data.lines)
-- set vscode controlled flag so we can check it neovim
api.nvim_buf_set_var(buf, "vscode_controlled", true)
-- In vscode same document can have different insertSpaces/tabSize settings
-- per editor; in Nvim it's per buffer. We assume here that these settings are
-- same for all editors.
api.nvim_buf_set_var(buf, "vscode_editor_options", data.editor_options)
api.nvim_buf_set_var(buf, "vscode_uri", data.uri)
api.nvim_buf_set_var(buf, "vscode_uri_data", data.uri_data)
-- force acwrite, which is similar to nofile, but will only be written via the
-- BufWriteCmd autocommand. #521 #1260
api.nvim_buf_set_option(buf, "buftype", "acwrite")
api.nvim_buf_set_option(buf, "buflisted", true)
api.nvim_buf_set_option(buf, "modifiable", data.modifiable)
api.nvim_buf_set_option(buf, "modified", data.modified)
force_filetype()
set_buffer_autocmd(buf)
end
---Reset undo tree for a buffer
-- Called from extension when opening/creating new file in vscode to reset undo tree
function M.clear_undo(buf)
local mod = vim.bo[buf].modified
local ul = vim.bo[buf].undolevels
api.nvim_buf_set_option(buf, "undolevels", -1)
api.nvim_buf_set_lines(buf, 0, 0, false, {})
api.nvim_buf_set_option(buf, "undolevels", ul)
api.nvim_buf_set_option(buf, "modified", mod)
end
--#endregion
return M

View File

@@ -0,0 +1,149 @@
--[[
1. Synchronize options: tabstop, shiftwidth, expandtab, number, relativenumber
Check for changes in nvim and then send them to vscode
Receive changes from vscode and set nvim options
2. Process modeline
The buffer's initial text is filled after creating the buffer,
the modeline processing needs to be triggered manually.
Additionally, the scratch buffer sets nomodeline by default.
]]
local api = vim.api
local vscode = require("vscode.api")
local util = require("vscode.util")
local M = {}
---@class EditorOptions
---@field tabSize number
---@field insertSpaces boolean
---@field lineNumbers "on"|"off"|"relative"
---@param nu boolean number
---@param rnu boolean relativenumber
---@return "on"|"off"|"relative"
local function get_number_style(nu, rnu)
return rnu and "relative" or nu and "on" or "off"
end
---@param win number
---@param lineNumbers "on"|"off"|"relative"
local function set_number(win, lineNumbers)
if lineNumbers == "relative" then
util.win_set_option(win, "relativenumber", true)
elseif lineNumbers == "on" then
util.win_set_option(win, "number", true)
util.win_set_option(win, "relativenumber", false)
else
util.win_set_option(win, "number", false)
util.win_set_option(win, "relativenumber", false)
end
end
---Handle changes from vscode
---Set nvim options
---@param buf number
---@param opts EditorOptions
local function set_options(buf, opts)
if not api.nvim_buf_is_valid(buf) then
return
end
api.nvim_buf_set_var(buf, "vscode_editor_options", opts)
util.buf_set_option(buf, "tabstop", opts.tabSize)
util.buf_set_option(buf, "shiftwidth", opts.tabSize)
util.buf_set_option(buf, "expandtab", opts.insertSpaces)
local win = api.nvim_get_current_win()
if api.nvim_win_get_buf(win) == buf then
set_number(win, opts.lineNumbers)
end
end
---Check changes from nvim
---Set vscode options
local function _check_options()
local vscode_opts = vim.b.vscode_editor_options
if not vscode_opts then -- should not happen
return
end
if not vim.b.vscode_editor_options_first_checked then --load the defaults
vim.b.vscode_editor_options_first_checked = true
util.buf_set_option(0, "tabstop", vscode_opts.tabSize)
util.buf_set_option(0, "shiftwidth", vscode_opts.tabSize)
util.buf_set_option(0, "expandtab", vscode_opts.insertSpaces)
set_number(0, vscode_opts.lineNumbers)
return
end
local buf = api.nvim_get_current_buf()
local ts, sw, et = vim.bo.ts, vim.bo.sw, vim.bo.et
if sw ~= ts then
util.buf_set_option(buf, "shiftwidth", ts) -- must be the same
end
local nvim_opts = {
tabSize = ts,
insertSpaces = et,
lineNumbers = get_number_style(vim.wo.nu, vim.wo.rnu),
}
if vim.deep_equal(nvim_opts, vscode_opts) then
return
end
vim.b.vscode_editor_options = nvim_opts
vscode.action("set_editor_options", { args = { buf, nvim_opts } })
end
local check_options = util.debounce(_check_options, 20)
local function process_modeline()
if vim.b.vscode_editor_options_first_checked then
if not vim.b.vscode_processed_modeline then
vim.b.vscode_processed_modeline = true
if vim.go.modeline then -- nomodeline by default for scratch buffer
vim.bo.modeline = true
end
vim.cmd.doautocmd("CursorMoved") -- process modeline
check_options()
end
end
end
function M.setup()
vscode.on("editor_options_changed", set_options)
vscode.on("document_buffer_init", function(buf)
if not api.nvim_buf_is_valid(buf) then
return
end
local has, opts = pcall(api.nvim_buf_get_var, buf, "vscode_editor_options")
if has then
set_options(buf, opts)
vim.defer_fn(process_modeline, 100)
end
end)
local group = api.nvim_create_augroup("vscode.sync-editor-options", { clear = true })
-- options
api.nvim_create_autocmd({ "OptionSet" }, {
group = group,
callback = check_options,
pattern = { "tabstop", "shiftwidth", "expandtab", "number", "relativenumber" },
})
api.nvim_create_autocmd(
{ "CursorMoved", "BufWinEnter", "InsertEnter", "InsertLeave", "FileType" },
{ group = group, callback = check_options }
)
-- modeline
api.nvim_create_autocmd({ "BufWinEnter", "WinEnter", "CursorMoved", "FileType" }, {
group = group,
callback = process_modeline,
})
end
return M

View File

@@ -0,0 +1,119 @@
local M = {}
local fn, api = vim.fn, vim.api
function M.is_visual_mode()
local mode = api.nvim_get_mode().mode
return mode == "v" or mode == "V" or mode == "\x16"
end
function M.get_char_at(line, byte_col, buf)
if not buf or buf == 0 then
buf = api.nvim_get_current_buf()
end
local line_str = fn.getbufoneline(buf, line)
local char_idx = fn.charidx(line_str, (byte_col - 1))
local char_nr = fn.strgetchar(line_str, char_idx)
if char_nr ~= -1 then
return fn.nr2char(char_nr)
else
return nil
end
end
--- Gets the zero-indexed lines from the given buffer.
---
---@param bufnr integer bufnr to get the lines from
---@param rows integer[] zero-indexed line numbers
---@return table<integer, string> a table mapping rows to lines
function M.get_lines(bufnr, rows)
rows = type(rows) == "table" and rows or { rows }
local lines = {}
for _, row in ipairs(rows) do
lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1]
end
return lines
end
--- Gets the zero-indexed line from the given buffer.
---
---@param bufnr integer
---@param row integer zero-indexed line number
---@return string the line at row in filename
function M.get_line(bufnr, row)
return M.get_lines(bufnr, { row })[row]
end
--- Compare two positions
---@param a lsp.Position
---@param b lsp.Position
---@return -1|0|1 -1 if a < b, 0 if a == b, 1 if a > b
function M.compare_position(a, b)
if a.line > b.line then
return 1
end
if a.line == b.line and a.character > b.character then
return 1
end
if a.line == b.line and a.character == b.character then
return 0
end
return -1
end
---Debounce a function.
---@param func function function to debounce
---@param time number trialing time in ms
---@return function
function M.debounce(func, time)
local timer
return function(...)
local args = { ... }
if timer and timer:is_active() then
timer:close()
end
timer = vim.defer_fn(function()
func(unpack(args))
end, time)
end
end
-- Since Nvim 0.10.0, `virtcol2col` changed from returning the last byte of a
-- multi-byte character to returning the first byte. However, we need the column
-- of the last byte, which is consistent with the selected region in Nvim.
-- See https://github.com/neovim/neovim/issues/29786
if fn.has("nvim-0.10.0") == 0 then
M.virtcol2col = fn.virtcol2col
else
---@diagnostic disable-next-line: duplicate-set-field
M.virtcol2col = function(winid, lnum, virtcol)
local byte_idx = fn.virtcol2col(winid, lnum, virtcol) - 1
local buf = api.nvim_win_get_buf(winid)
local line = M.get_line(buf, lnum - 1)
local char_idx = fn.charidx(line, byte_idx)
local prefix = fn.strcharpart(line, 0, char_idx + 1)
return #prefix
end
end
-- Wrapper for nvim_set_option_value that sets the option only if the value differs
function M.set_option_value(name, value, opts)
opts = opts or {}
local current = api.nvim_get_option_value(name, opts)
if current ~= value then
api.nvim_set_option_value(name, value, opts)
end
end
-- Wrapper for set_option_value to set a buffer option
function M.buf_set_option(buf, name, value)
return M.set_option_value(name, value, { buf = buf })
end
-- Wrapper for set_option_value to set a window option
function M.win_set_option(win, name, value)
return M.set_option_value(name, value, { win = win })
end
return M

View File

@@ -0,0 +1,93 @@
local M = {}
local api, fn = vim.api, vim.fn
M.event_group = api.nvim_create_augroup("vscode.viewport", { clear = true })
M.viewport_changed_ns = api.nvim_create_namespace("vscode.viewport.changed")
---@class WinView All positions are 0-based
---@field winid integer
---@field bufnr integer
---@field lnum integer
---@field col integer
---@field coladd integer
---@field curswant integer
---@field topline integer
---@field botline integer
---@field topfill integer
---@field leftcol integer
---@field skipcol integer
local function setup_viewport_changed()
---@type table<integer, WinView>
local view_cache = {}
api.nvim_set_decoration_provider(M.viewport_changed_ns, {
on_win = function(_, win, buf, topline, botline)
-- We don't need the first window.
if win == 1000 then
return
end
---@type WinView
local view = api.nvim_win_call(win, fn.winsaveview)
view.winid = win
view.bufnr = buf
view.topline = topline
view.botline = botline
view.lnum = view.lnum - 1
local cache = view_cache[view.winid]
if cache and vim.deep_equal(view, cache) then
return
end
view_cache[view.winid] = view
--#region XXX: Temporary fix for #2165
-- Avoid unnecessary notifications
-- For highlighting #1976
local leftcol_changed = cache and cache.leftcol ~= view.leftcol
-- For highlighting #2194
local topline_changed = cache and cache.topline ~= view.topline
-- For cursor position #1971
local cursor_changed = cache
and (cache.lnum ~= view.lnum or cache.col ~= cache.col)
and api.nvim_get_mode().mode == "c"
if not leftcol_changed and not cursor_changed and not topline_changed then
return
end
--#endregion
fn.VSCodeExtensionNotify("viewport-changed", view)
end,
})
-- cleanup cache
api.nvim_create_autocmd({ "WinClosed" }, {
group = M.event_group,
callback = function()
local wins = vim.tbl_keys(view_cache)
for _, win in ipairs(wins) do
if not api.nvim_win_is_valid(win) then
view_cache[win] = nil
end
end
end,
})
end
function M.setup()
-- Highlighting needs to wait for the viewport-changed event to complete.
-- When the UI attaches, there are numerous highlight events (hl_attr_define, grid_line) to process.
--
-- Without delaying the setup, the viewport-changed event will cause frequent
-- pauses in highlight processing, resulting in screen flickering.
api.nvim_create_autocmd({ "UIEnter" }, {
once = true,
callback = function()
-- Don't worry about whether it's set too late.
vim.defer_fn(setup_viewport_changed, 1000)
end,
})
end
return M

View File

@@ -0,0 +1,156 @@
" Set global flag to allow checking in custom user config
let g:vscode = 1
" Setup runtimepath
let g:vscode_neovim_runtime = fnamemodify(resolve(expand('<sfile>:p')), ':h')
lua << EOF
-- Detect potential module conflicts
local dirs = vim.api.nvim_get_runtime_file("lua/vscode/", true)
local files = vim.api.nvim_get_runtime_file("lua/vscode.lua", true)
if #dirs > 0 or #files > 0 then
for _, file in ipairs(files) do
table.insert(dirs, file)
end
local msg = "vscode-neovim uses module name `vscode` which conflicts with existing modules. Please rename the module. "
.. table.concat(dirs, ", ")
local cmd = "await vscode.window.showErrorMessage(args)"
vim.rpcnotify(vim.g.vscode_channel, "vscode-action", "eval", { args = { cmd, msg } })
end
vim.opt.rtp:prepend(vim.g.vscode_neovim_runtime)
EOF
" Check version
lua << EOF
local MIN_VERSION = vim.g.vscode_nvim_min_version
local cmp = -1 ---@type -1|0|1
local prerelease = false
if vim.version and vim.version.parse then
local v = vim.version()
local curr = { v.major, v.minor, v.patch }
local min = vim.version.parse(MIN_VERSION)
cmp = vim.version.cmp(curr, min)
prerelease = not not v.prerelease
end
local warn = true
local msgs = {
("vscode-neovim requires nvim version %s or higher."):format(MIN_VERSION),
"Install the [latest stable version](https://github.com/neovim/neovim/releases/latest).",
}
if cmp < 0 then
-- nothing
elseif cmp == 0 and prerelease then
table.insert(msgs, 2, ("Current version is a **prerelease** of %s.").format(MIN_VERSION))
else
warn = false
end
if warn then
vim.rpcnotify(vim.g.vscode_channel, "vscode-action", "eval", {
args = { "vscode.window.showErrorMessage(args)", table.concat(msgs, " ") },
})
end
EOF
" Load altercmd
" {{{
" altercmd - Alter built-in Ex commands by your own ones
" Version: 0.0.1
" Copyright (C) 2009-2015 Kana Natsuno <http://whileimautomaton.net/>
" License: MIT license
function! s:altercmd_define(...)
let [buffer, original_name, alternate_name]
\ = (a:000[0] ==? '<buffer>' ? [] : ['']) + a:000
if original_name =~ '\['
let [original_name_head, original_name_tail] = split(original_name, '[')
let original_name_tail = substitute(original_name_tail, '\]', '', '')
else
let original_name_head = original_name
let original_name_tail = ''
endif
let original_name_tail = ' ' . original_name_tail
for i in range(len(original_name_tail))
let lhs = original_name_head . original_name_tail[1:i]
execute 'cnoreabbrev <expr>' buffer lhs
\ '(getcmdtype() == ":" && getcmdline() ==# "' . lhs . '")'
\ '?' ('"' . alternate_name . '"')
\ ':' ('"' . lhs . '"')
endfor
endfunction
command! -bar -complete=command -nargs=* AlterCommand call s:altercmd_define(<f-args>)
" }}}
" RPC and global functions
" internal
function! VSCodeExtensionNotify(cmd, ...)
call rpcnotify(g:vscode_channel, 'vscode-neovim', a:cmd, a:000)
endfunction
" apis
function! VSCodeCall(cmd, ...) abort
call luaeval('require"vscode".call(_A[1], {args = _A[2]})', [a:cmd, a:000])
endfunction
function! VSCodeNotify(cmd, ...)
call luaeval('require"vscode".action(_A[1], {args = _A[2]})', [a:cmd, a:000])
endfunction
function! VSCodeCallRange(cmd, line1, line2, leaveSelection, ...) abort
call luaeval('require"vscode".call(_A[1], { range = _A[2], restore_selection=_A[3], args = _A[4] })',
\ [a:cmd, [a:line1 - 1, a:line2 - 1], a:leaveSelection ? v:false : v:true, a:000])
endfunction
function! VSCodeNotifyRange(cmd, line1, line2, leaveSelection, ...)
call luaeval('require"vscode".action(_A[1], { range = _A[2], restore_selection=_A[3], args = _A[4] })',
\ [a:cmd, [a:line1 - 1, a:line2 - 1], a:leaveSelection ? v:false : v:true, a:000])
endfunction
function! VSCodeCallRangePos(cmd, line1, line2, pos1, pos2, leaveSelection, ...) abort
call luaeval('require"vscode".call(_A[1], { range = _A[2], restore_selection=_A[3], args = _A[4] })',
\ [a:cmd, [a:line1 - 1, a:pos1 - 1, a:line2 - 1, a:pos2 - 1], a:leaveSelection ? v:false : v:true, a:000])
endfunction
function! VSCodeNotifyRangePos(cmd, line1, line2, pos1, pos2, leaveSelection, ...)
call luaeval('require"vscode".action(_A[1], { range = _A[2], restore_selection=_A[3], args = _A[4] })',
\ [a:cmd, [a:line1 - 1, a:pos1 - 1, a:line2 - 1, a:pos2 - 1], a:leaveSelection ? v:false : v:true, a:000])
endfunction
function! s:onInsertEnter()
let reg = reg_recording()
if !empty(reg)
call VSCodeExtensionNotify('notify-recording', reg)
endif
" Hack to disable `type` unbinding during insert mode by triggering recording mode to add support for vim-visual-multi
" https://github.com/vscode-neovim/vscode-neovim/pull/1755
if exists("b:VM_Selection") && !empty(b:VM_Selection)
call VSCodeExtensionNotify('notify-recording', reg)
endif
endfunction
augroup VscodeGeneral
autocmd!
autocmd BufWinEnter * call VSCodeExtensionNotify('external-buffer', getbufinfo(bufnr())[0], &et, &ts)
autocmd InsertEnter * call <SID>onInsertEnter()
" Trigger filetype detection
autocmd BufAdd * do BufRead
autocmd VimEnter,ModeChanged * call VSCodeExtensionNotify('mode-changed', mode())
autocmd WinEnter * call VSCodeExtensionNotify('window-changed', win_getid())
" LazyVim will clear runtimepath by default. To avoid user intervention, we need to set it again.
autocmd User LazyDone let &runtimepath = g:vscode_neovim_runtime . ',' . &runtimepath
" Source config "afterInitConfig"
autocmd VimEnter * call nvim_exec2(join(v:lua.require("vscode").get_config("vscode-neovim.afterInitConfig"), "\n"), {})
augroup END
" TODO: Remove when dropping support for nvim 0.9
lua vim.islist = vim.islist or vim.tbl_islist
lua require("vscode")
runtime! vscode/**/*.{vim,lua}

View File

@@ -0,0 +1,42 @@
local code = require("vscode")
local last_item = nil
local function paste()
local curr_text = code.eval("return await vscode.env.clipboard.readText()"):gsub("\r\n", "\n")
local curr_item = { vim.split(curr_text, "\n"), "v" }
if not last_item then
return curr_item
end
if not vim.deep_equal(last_item[1], curr_item[1]) then
return curr_item
end
return last_item
end
local function copy(lines, regtype)
last_item = { lines, regtype }
local text = table.concat(lines, "\n")
code.eval("await vscode.env.clipboard.writeText(args)", { args = text })
end
vim.g.vscode_clipboard = {
name = "VSCodeClipboard",
copy = {
["+"] = copy,
["*"] = copy,
},
paste = {
["+"] = paste,
["*"] = paste,
},
cache_enabled = 0,
}
-- The user also can override g:clipboard in the init config
if vim.fn.has("wsl") == 1 then
vim.g.clipboard = vim.g.vscode_clipboard
end

View File

@@ -0,0 +1,53 @@
local vscode = require("vscode")
local function esc()
local key = vim.api.nvim_replace_termcodes("<esc>", true, true, true)
vim.api.nvim_feedkeys(key, "n", false)
end
local k = function(mode, lhs, rhs)
vim.keymap.set(mode, lhs, rhs, { expr = true }) -- expr is required
end
------------
-- Format --
------------
local format = vscode.to_op(function(ctx)
vscode.action("editor.action.formatSelection", { range = ctx.range, callback = esc })
end)
local format_line = function()
return format() .. "_"
end
k({ "n", "x" }, "gq", format)
k({ "n" }, "gqq", format_line)
k({ "n", "x" }, "=", format)
k({ "n" }, "==", format_line)
-------------
-- Comment --
-------------
local comment = vscode.to_op(function(ctx)
local cmd = ctx.is_linewise and "editor.action.commentLine" or "editor.action.blockComment"
local opts = { range = ctx.range, callback = esc }
if ctx.is_linewise and ctx.is_current_line then
opts.range = nil
end
vscode.action(cmd, opts)
end)
local comment_line = function()
return comment() .. "_"
end
k({ "n", "x" }, "gc", comment)
k({ "n" }, "gcc", comment_line)
k({ "x" }, "<C-/>", comment)
k({ "n" }, "<C-/>", comment_line)
-- legacy {{{
k({ "n", "x" }, "<Plug>VSCodeCommentary", comment)
k({ "n" }, "<Plug>VSCodeCommentaryLine", comment_line)
vim.api.nvim_create_user_command("VSCodeCommentary", function(arg)
vscode.action("editor.action.commentLine", { range = { arg.line1 - 1, arg.line2 - 1 } })
end, { bang = true, range = true })
-- }}}

View File

@@ -0,0 +1,14 @@
vim.filetype.add({
pattern = {
-- To ensure that the user's fallback pattern is not overridden.
[".*.*.*"] = {
priority = -math.huge,
function(_, bufnr)
local ok, filetype = pcall(vim.api.nvim_buf_get_var, bufnr, "vscode_filetype")
if ok and filetype then
return filetype
end
end,
},
},
})

View File

@@ -0,0 +1,56 @@
-- Support preview list when inccommand is set to split. Mainly for 'substitute'
local api, fn = vim.api, vim.fn
local function get_lines()
-- Find preview buffer --
local preview_buf
local bufs = api.nvim_list_bufs()
for _, buf in ipairs(bufs) do
if
vim.bo[buf].buftype == "nofile"
and fn.bufname(buf) == "[Preview]"
and fn.getbufoneline(buf, 1):match("%d+|")
then
preview_buf = buf
break
end
end
-- return items --
if not preview_buf then
return
end
return api.nvim_buf_get_lines(preview_buf, 0, -1, false)
end
local re = vim.regex([[\(s\|su\|substitute\)/.]])
local function refresh_completion()
if
not vim.g.vscode_channel
or vim.opt.icm:get() ~= "split"
or fn.getcmdtype() ~= ":"
or not re:match_str(fn.getcmdline())
then
return
end
local lines = get_lines()
if lines then
local items = {}
for _, line in ipairs(lines) do
table.insert(items, { line, "", "", "" })
end
vim.rpcnotify(vim.g.vscode_channel, "redraw", { "popupmenu_show", { items, -1, 0, 0, -1 } })
end
end
api.nvim_create_autocmd({ "CmdlineChanged" }, {
group = api.nvim_create_augroup("vscode.inccommand", {}),
callback = function()
vim.schedule(refresh_completion)
end,
})

View File

@@ -0,0 +1,10 @@
local code = require("vscode")
vim.api.nvim_create_autocmd("InsertLeave", {
group = vim.api.nvim_create_augroup("vscode.integrations", { clear = true }),
callback = function()
code.action("hideSuggestWidget")
code.action("closeParameterHints")
code.action("editor.action.inlineSuggest.hide")
end,
})

View File

@@ -0,0 +1,77 @@
--- Shim vim.lsp.buf calls to vscode equivalent commands.
local vscode = require("vscode")
local M = {}
local map = {
hover = "editor.action.showHover",
declaration = "editor.action.peekDeclaration",
definition = "editor.action.peekDefinition",
type_definition = "editor.action.peekTypeDefinition",
implementation = "editor.action.peekImplementation",
signature_help = "editor.action.triggerParameterHints",
completion = "editor.action.triggerSuggest",
format = "editor.action.formatDocument",
rename = "editor.action.rename",
references = "editor.action.referenceSearch.trigger",
document_symbol = "workbench.action.gotoSymbol",
incoming_calls = "editor.showIncomingCalls",
outgoing_calls = "editor.showOutgoingCalls",
---@param kind "subtypes"|"supertypes"
typehierarchy = function(kind)
local cmd
if kind == "subtypes" then
cmd = "editor.showSubtypes"
elseif kind == "supertypes" then
cmd = "editor.showSupertypes"
else
cmd = "editor.showTypeHierarchy"
end
vscode.action(cmd)
end,
list_workspace_folders = function()
return vscode.eval("return (vscode.workspace.workspaceFolders || []).map((folder) => folder.name);")
end,
add_workspace_folder = "workbench.action.addRootFolder",
remove_workspace_folder = "workbench.action.removeRootFolder",
workspace_symbol = function(query)
vscode.action("workbench.action.quickOpen", { args = { "#" .. (query or "") } })
end,
document_highlight = "editor.action.wordHighlight.trigger",
clear_references = vim.NIL,
code_action = "editor.action.sourceAction",
execute_command = vim.NIL,
}
for method, cmd in pairs(map) do
local cmd_type = type(cmd)
if cmd == vim.NIL then
M[method] = function()
vscode.notify(string.format("vim.lsp.buf.%s is not supported in vscode.", method), vim.log.levels.WARN)
end
elseif cmd_type == "string" then
M[method] = function()
vscode.action(cmd)
end
elseif cmd_type == "function" then
M[method] = cmd
end
end
vim.lsp.buf = setmetatable(M, {
__index = function(t, method)
t[method] = function()
vscode.notify(
string.format(
"vim.lsp.buf.%s is not handled by vscode-neovim. %s",
method,
"Please report this issue at [vscode-neovim](https://github.com/vscode-neovim/vscode-neovim/issues)"
),
vim.log.levels.WARN
)
end
return t[method]
end,
})

View File

@@ -0,0 +1,38 @@
function! s:vscodeGoToDefinition(str)
if exists('b:vscode_controlled') && b:vscode_controlled
call VSCodeNotify('editor.action.' . a:str)
else
" Allow to function in help files
exe "normal! \<C-]>"
endif
endfunction
" gf/gF . Map to go to definition for now
nnoremap K <Cmd>call VSCodeNotify('editor.action.showHover')<CR>
nnoremap gh <Cmd>call VSCodeNotify('editor.action.showHover')<CR>
nnoremap gf <Cmd>call <SID>vscodeGoToDefinition('revealDeclaration')<CR>
nnoremap gd <Cmd>call <SID>vscodeGoToDefinition('revealDefinition')<CR>
nnoremap <C-]> <Cmd>call <SID>vscodeGoToDefinition('revealDefinition')<CR>
nnoremap gO <Cmd>call VSCodeNotify('workbench.action.gotoSymbol')<CR>
nnoremap gF <Cmd>call VSCodeNotify('editor.action.peekDeclaration')<CR>
nnoremap gD <Cmd>call VSCodeNotify('editor.action.peekDefinition')<CR>
nnoremap gH <Cmd>call VSCodeNotify('editor.action.referenceSearch.trigger')<CR>
xnoremap K <Cmd>call VSCodeNotify('editor.action.showHover')<CR>
xnoremap gh <Cmd>call VSCodeNotify('editor.action.showHover')<CR>
xnoremap gf <Cmd>call <SID>vscodeGoToDefinition('revealDeclaration')<CR>
xnoremap gd <Cmd>call <SID>vscodeGoToDefinition('revealDefinition')<CR>
xnoremap <C-]> <Cmd>call <SID>vscodeGoToDefinition('revealDefinition')<CR>
xnoremap gO <Cmd>call VSCodeNotify('workbench.action.gotoSymbol')<CR>
xnoremap gF <Cmd>call VSCodeNotify('editor.action.peekDeclaration')<CR>
xnoremap gD <Cmd>call VSCodeNotify('editor.action.peekDefinition')<CR>
xnoremap gH <Cmd>call VSCodeNotify('editor.action.referenceSearch.trigger')<CR>
" <C-w> gf opens definition on the side
nnoremap <C-w>gf <Cmd>call VSCodeNotify('editor.action.revealDefinitionAside')<CR>
nnoremap <C-w>gd <Cmd>call VSCodeNotify('editor.action.revealDefinitionAside')<CR>
xnoremap <C-w>gf <Cmd>call VSCodeNotify('editor.action.revealDefinitionAside')<CR>
xnoremap <C-w>gd <Cmd>call VSCodeNotify('editor.action.revealDefinitionAside')<CR>
" open quickfix menu for spelling corrections and refactoring
nnoremap z= <Cmd>call VSCodeNotify('editor.action.quickFix')<CR>

View File

@@ -0,0 +1,56 @@
function! s:editOrNew(...)
let file = a:1
let bang = a:2
if empty(file)
if bang ==# '!'
call VSCodeNotify('workbench.action.files.revert')
else
call VSCodeNotify('workbench.action.quickOpen')
endif
else
" Last arg is to close previous file, e.g. e! ~/blah.txt will open blah.txt instead the current file
call VSCodeExtensionNotify('open-file', expand(file), bang ==# '!' ? 1 : 0)
endif
endfunction
function! s:saveAndClose() abort
call VSCodeCall('workbench.action.files.save')
call VSCodeNotify('workbench.action.closeActiveEditor')
endfunction
function! s:saveAllAndClose() abort
call VSCodeCall('workbench.action.files.saveAll')
call VSCodeNotify('workbench.action.closeAllEditors')
endfunction
" command! -bang -nargs=? Edit call VSCodeCall('workbench.action.quickOpen')
command! -complete=file -bang -nargs=? Edit call <SID>editOrNew(<q-args>, <q-bang>)
command! -bang -nargs=? Ex call <SID>editOrNew(<q-args>, <q-bang>)
command! -bang Enew call <SID>editOrNew('__vscode_new__', <q-bang>)
command! -bang Find call VSCodeNotify('workbench.action.quickOpen')
command! -bang Quit if <q-bang> ==# '!' | call VSCodeNotify('workbench.action.revertAndCloseActiveEditor') | else | call VSCodeNotify('workbench.action.closeActiveEditor') | endif
command! -bang Wq call <SID>saveAndClose()
command! -bang Xit call <SID>saveAndClose()
command! -bang Qall call VSCodeNotify('workbench.action.closeAllEditors')
command! -bang Wqall call <SID>saveAllAndClose()
command! -bang Xall call <SID>saveAllAndClose()
AlterCommand e[dit] Edit
AlterCommand ex Ex
AlterCommand ene[w] Enew
AlterCommand fin[d] Find
AlterCommand q[uit] Quit
AlterCommand wq Wq
AlterCommand x[it] Xit
AlterCommand qa[ll] Qall
AlterCommand wqa[ll] Wqall
AlterCommand xa[ll] Xall
nnoremap ZZ <Cmd>Wq<CR>
nnoremap ZQ <Cmd>Quit!<CR>

View File

@@ -0,0 +1,13 @@
function! s:jump(...)
let count = a:1
let to = a:2
for i in range(1, count ? count : 1)
call VSCodeNotify(to ==# 'back' ? 'workbench.action.navigateBack' : 'workbench.action.navigateForward')
endfor
endfunction
nnoremap <silent> <C-o> <Cmd>call <SID>jump(v:count, 'back')<CR>
nnoremap <silent> <C-t> <Cmd>call <SID>jump(v:count, 'back')<CR>
nnoremap <silent> <C-i> <Cmd>call <SID>jump(v:count, 'forward')<CR>
nnoremap <silent> <Tab> <Cmd>call <SID>jump(v:count, 'forward')<CR>

View File

@@ -0,0 +1,16 @@
function! s:toFirstCharOfScreenLine()
call VSCodeNotify('cursorMove', { 'to': 'wrappedLineFirstNonWhitespaceCharacter' })
endfunction
function! s:toLastCharOfScreenLine()
call VSCodeNotify('cursorMove', { 'to': 'wrappedLineLastNonWhitespaceCharacter' })
" Offfset cursor moving to the right caused by calling VSCode command in Vim mode
call VSCodeNotify('cursorLeft')
endfunction
nnoremap g0 <Cmd>call <SID>toFirstCharOfScreenLine()<CR>
nnoremap g$ <Cmd>call <SID>toLastCharOfScreenLine()<CR>
" Note: Using these in macro will break it
nnoremap gk <Cmd>call VSCodeNotify('cursorMove', { 'to': 'up', 'by': 'wrappedLine', 'value': v:count1 })<CR>
nnoremap gj <Cmd>call VSCodeNotify('cursorMove', { 'to': 'down', 'by': 'wrappedLine', 'value': v:count1 })<CR>

View File

@@ -0,0 +1,46 @@
function s:reveal(direction, resetCursor)
call VSCodeExtensionNotify('reveal', a:direction, a:resetCursor)
endfunction
nnoremap z<CR> <Cmd>call <SID>reveal('top', 1)<CR>
xnoremap z<CR> <Cmd>call <SID>reveal('top', 1)<CR>
nnoremap zt <Cmd>call <SID>reveal('top', 0)<CR>
xnoremap zt <Cmd>call <SID>reveal('top', 0)<CR>
nnoremap z. <Cmd>call <SID>reveal('center', 1)<CR>
xnoremap z. <Cmd>call <SID>reveal('center', 1)<CR>
nnoremap zz <Cmd>call <SID>reveal('center', 0)<CR>
xnoremap zz <Cmd>call <SID>reveal('center', 0)<CR>
nnoremap z- <Cmd>call <SID>reveal('bottom', 1)<CR>
xnoremap z- <Cmd>call <SID>reveal('bottom', 1)<CR>
nnoremap zb <Cmd>call <SID>reveal('bottom', 0)<CR>
xnoremap zb <Cmd>call <SID>reveal('bottom', 0)<CR>
function s:moveCursor(to)
" Native VSCode commands don't register jumplist. Fix by registering jumplist in Vim e.g. for subsequent use of <C-o>
normal! m'
call VSCodeExtensionNotify('move-cursor', a:to)
endfunction
nnoremap H <Cmd>call <SID>moveCursor('top')<CR>
xnoremap H <Cmd>call <SID>moveCursor('top')<CR>
nnoremap M <Cmd>call <SID>moveCursor('middle')<CR>
xnoremap M <Cmd>call <SID>moveCursor('middle')<CR>
nnoremap L <Cmd>call <SID>moveCursor('bottom')<CR>
xnoremap L <Cmd>call <SID>moveCursor('bottom')<CR>
" Disabled due to scroll problems (the ext binds them directly)
" nnoremap <silent> <expr> <C-d> VSCodeExtensionNotify('scroll', 'halfPage', 'down')
" xnoremap <silent> <expr> <C-d> VSCodeExtensionNotify('scroll', 'halfPage', 'down')
" nnoremap <silent> <expr> <C-u> VSCodeExtensionNotify('scroll', 'halfPage', 'up')
" xnoremap <silent> <expr> <C-u> VSCodeExtensionNotify('scroll', 'halfPage', 'up')
" nnoremap <silent> <expr> <C-f> VSCodeExtensionNotify('scroll', 'page', 'down')
" xnoremap <silent> <expr> <C-f> VSCodeExtensionNotify('scroll', 'page', 'down')
" nnoremap <silent> <expr> <C-b> VSCodeExtensionNotify('scroll', 'page', 'up')
" xnoremap <silent> <expr> <C-b> VSCodeExtensionNotify('scroll', 'page', 'up')
" nnoremap <silent> <expr> <C-e> VSCodeExtensionNotify('scroll-line', 'down')
" xnoremap <silent> <expr> <C-e> VSCodeExtensionNotify('scroll-line', 'down')
" nnoremap <silent> <expr> <C-y> VSCodeExtensionNotify('scroll-line', 'up')
" xnoremap <silent> <expr> <C-y> VSCodeExtensionNotify('scroll-line', 'up')

View File

@@ -0,0 +1,46 @@
function! s:switchEditor(...) abort
let count = a:1
let direction = a:2
for i in range(1, count ? count : 1)
call VSCodeCall(direction ==# 'next' ? 'workbench.action.nextEditorInGroup' : 'workbench.action.previousEditorInGroup')
endfor
endfunction
function! s:gotoEditor(...) abort
let count = a:1
call VSCodeCall(count ? 'workbench.action.openEditorAtIndex' . count : 'workbench.action.nextEditorInGroup')
endfunction
command! -complete=file -nargs=? Tabedit if empty(<q-args>) | call VSCodeNotify('workbench.action.quickOpen') | else | call VSCodeExtensionNotify('open-file', expand(<q-args>), 0) | endif
command! -complete=file -nargs=? Tabnew call VSCodeExtensionNotify('open-file', empty(<q-args>) ? '__vscode_new__' : expand(<q-args>), 0)
command! Tabfind call VSCodeNotify('workbench.action.quickOpen')
command! Tab echoerr 'Not supported'
command! Tabs echoerr 'Not supported'
command! -bang Tabclose if <q-bang> ==# '!' | call VSCodeNotify('workbench.action.revertAndCloseActiveEditor') | else | call VSCodeNotify('workbench.action.closeActiveEditor') | endif
command! Tabonly call VSCodeNotify('workbench.action.closeOtherEditors')
command! -nargs=? Tabnext call <SID>switchEditor(<q-args>, 'next')
command! -nargs=? Tabprevious call <SID>switchEditor(<q-args>, 'prev')
command! Tabfirst call VSCodeNotify('workbench.action.firstEditorInGroup')
command! Tablast call VSCodeNotify('workbench.action.lastEditorInGroup')
command! Tabrewind call VSCodeNotify('workbench.action.firstEditorInGroup')
command! -nargs=? Tabmove echoerr 'Not supported yet'
AlterCommand tabe[dit] Tabedit
AlterCommand tabnew Tabnew
AlterCommand tabf[ind] Tabfind
AlterCommand tab Tab
AlterCommand tabs Tabs
AlterCommand tabc[lose] Tabclose
AlterCommand tabo[nly] Tabonly
AlterCommand tabn[ext] Tabnext
AlterCommand tabp[revious] Tabprevious
AlterCommand tabr[ewind] Tabrewind
AlterCommand tabfir[st] Tabfirst
AlterCommand tabl[ast] Tablast
AlterCommand tabm[ove] Tabmove
nnoremap gt <Cmd>call <SID>gotoEditor(v:count, 'next')<CR>
xnoremap gt <Cmd>call <SID>gotoEditor(v:count, 'next')<CR>
nnoremap gT <Cmd>call <SID>switchEditor(v:count, 'prev')<CR>
xnoremap gT <Cmd>call <SID>switchEditor(v:count, 'prev')<CR>

View File

@@ -0,0 +1,158 @@
function! s:split(...) abort
let direction = a:1
let file = exists('a:2') ? a:2 : ''
call VSCodeCall(direction ==# 'h' ? 'workbench.action.splitEditorDown' : 'workbench.action.splitEditorRight')
if !empty(file)
call VSCodeExtensionNotify('open-file', expand(file), 'all')
endif
endfunction
function! s:splitNew(...)
let file = a:2
call s:split(a:1, empty(file) ? '__vscode_new__' : file)
endfunction
function! s:closeOtherEditors()
call VSCodeNotify('workbench.action.closeEditorsInOtherGroups')
call VSCodeNotify('workbench.action.closeOtherEditors')
endfunction
function! s:manageEditorHeight(...)
let count = a:1
let to = a:2
for i in range(1, count ? count : 1)
call VSCodeNotify(to ==# 'increase' ? 'workbench.action.increaseViewHeight' : 'workbench.action.decreaseViewHeight')
endfor
endfunction
function! s:manageEditorWidth(...)
let count = a:1
let to = a:2
for i in range(1, count ? count : 1)
call VSCodeNotify(to ==# 'increase' ? 'workbench.action.increaseViewWidth' : 'workbench.action.decreaseViewWidth')
endfor
endfunction
command! -complete=file -nargs=? Split call <SID>split('h', <q-args>)
command! -complete=file -nargs=? Vsplit call <SID>split('v', <q-args>)
command! -complete=file -nargs=? New call <SID>split('h', '__vscode_new__')
command! -complete=file -nargs=? Vnew call <SID>split('v', '__vscode_new__')
command! -bang Only if <q-bang> ==# '!' | call <SID>closeOtherEditors() | else | call VSCodeNotify('workbench.action.joinAllGroups') | endif
AlterCommand sp[lit] Split
AlterCommand vs[plit] Vsplit
AlterCommand new New
AlterCommand vne[w] Vnew
AlterCommand on[ly] Only
" buffer management
nnoremap <C-w>n <Cmd>call <SID>splitNew('h', '__vscode_new__')<CR>
xnoremap <C-w>n <Cmd>call <SID>splitNew('h', '__vscode_new__')<CR>
nnoremap <C-w>q <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
xnoremap <C-w>q <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
nnoremap <C-w>c <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
xnoremap <C-w>c <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
nnoremap <C-w><C-c> <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
xnoremap <C-w><C-c> <Cmd>call VSCodeNotify('workbench.action.closeActiveEditor')<CR>
" window/splits management
nnoremap <C-w>s <Cmd>call <SID>split('h')<CR>
xnoremap <C-w>s <Cmd>call <SID>split('h')<CR>
nnoremap <C-w><C-s> <Cmd>call <SID>split('h')<CR>
xnoremap <C-w><C-s> <Cmd>call <SID>split('h')<CR>
nnoremap <C-w>v <Cmd>call <SID>split('v')<CR>
xnoremap <C-w>v <Cmd>call <SID>split('v')<CR>
nnoremap <C-w><C-v> <Cmd>call <SID>split('v')<CR>
xnoremap <C-w><C-v> <Cmd>call <SID>split('v')<CR>
nnoremap <C-w>= <Cmd>call VSCodeNotify('workbench.action.evenEditorWidths')<CR>
xnoremap <C-w>= <Cmd>call VSCodeNotify('workbench.action.evenEditorWidths')<CR>
nnoremap <C-w>_ <Cmd>call VSCodeNotify('workbench.action.toggleEditorWidths')<CR>
xnoremap <C-w>_ <Cmd>call VSCodeNotify('workbench.action.toggleEditorWidths')<CR>
nnoremap <C-w>+ <Cmd>call <SID>manageEditorHeight(v:count, 'increase')<CR>
xnoremap <C-w>+ <Cmd>call <SID>manageEditorHeight(v:count, 'increase')<CR>
nnoremap <C-w>- <Cmd>call <SID>manageEditorHeight(v:count, 'decrease')<CR>
xnoremap <C-w>- <Cmd>call <SID>manageEditorHeight(v:count, 'decrease')<CR>
nnoremap <C-w>> <Cmd>call <SID>manageEditorWidth(v:count, 'increase')<CR>
xnoremap <C-w>> <Cmd>call <SID>manageEditorWidth(v:count, 'increase')<CR>
nnoremap <C-w>< <Cmd>call <SID>manageEditorWidth(v:count, 'decrease')<CR>
xnoremap <C-w>< <Cmd>call <SID>manageEditorWidth(v:count, 'decrease')<CR>
nnoremap <C-w>o <Cmd>call VSCodeNotify('workbench.action.joinAllGroups')<CR>
xnoremap <C-w>o <Cmd>call VSCodeNotify('workbench.action.joinAllGroups')<CR>
nnoremap <C-w><C-o> <Cmd>call VSCodeNotify('workbench.action.joinAllGroups')<CR>
xnoremap <C-w><C-o> <Cmd>call VSCodeNotify('workbench.action.joinAllGroups')<CR>
" window navigation
nnoremap <C-w>j <Cmd>call VSCodeNotify('workbench.action.navigateDown')<CR>
xnoremap <C-w>j <Cmd>call VSCodeNotify('workbench.action.navigateDown')<CR>
nnoremap <C-w>k <Cmd>call VSCodeNotify('workbench.action.navigateUp')<CR>
xnoremap <C-w>k <Cmd>call VSCodeNotify('workbench.action.navigateUp')<CR>
nnoremap <C-w>h <Cmd>call VSCodeNotify('workbench.action.navigateLeft')<CR>
xnoremap <C-w>h <Cmd>call VSCodeNotify('workbench.action.navigateLeft')<CR>
nnoremap <C-w>l <Cmd>call VSCodeNotify('workbench.action.navigateRight')<CR>
xnoremap <C-w>l <Cmd>call VSCodeNotify('workbench.action.navigateRight')<CR>
nnoremap <C-w><Down> <Cmd>call VSCodeNotify('workbench.action.navigateDown')<CR>
xnoremap <C-w><Down> <Cmd>call VSCodeNotify('workbench.action.navigateDown')<CR>
nnoremap <C-w><Up> <Cmd>call VSCodeNotify('workbench.action.navigateUp')<CR>
xnoremap <C-w><Up> <Cmd>call VSCodeNotify('workbench.action.navigateUp')<CR>
nnoremap <C-w><Left> <Cmd>call VSCodeNotify('workbench.action.navigateLeft')<CR>
xnoremap <C-w><Left> <Cmd>call VSCodeNotify('workbench.action.navigateLeft')<CR>
nnoremap <C-w><Right> <Cmd>call VSCodeNotify('workbench.action.navigateRight')<CR>
xnoremap <C-w><Right> <Cmd>call VSCodeNotify('workbench.action.navigateRight')<CR>
nnoremap <C-w><C-j> <Cmd>call VSCodeNotify('workbench.action.moveEditorToBelowGroup')<CR>
xnoremap <C-w><C-j> <Cmd>call VSCodeNotify('workbench.action.moveEditorToBelowGroup')<CR>
nnoremap <C-w><C-k> <Cmd>call VSCodeNotify('workbench.action.moveEditorToAboveGroup')<CR>
xnoremap <C-w><C-k> <Cmd>call VSCodeNotify('workbench.action.moveEditorToAboveGroup')<CR>
nnoremap <C-w><C-h> <Cmd>call VSCodeNotify('workbench.action.moveEditorToLeftGroup')<CR>
xnoremap <C-w><C-h> <Cmd>call VSCodeNotify('workbench.action.moveEditorToLeftGroup')<CR>
nnoremap <C-w><C-l> <Cmd>call VSCodeNotify('workbench.action.moveEditorToRightGroup')<CR>
xnoremap <C-w><C-l> <Cmd>call VSCodeNotify('workbench.action.moveEditorToRightGroup')<CR>
nnoremap <C-w><C-Down> <Cmd>call VSCodeNotify('workbench.action.moveEditorToBelowGroup')<CR>
xnoremap <C-w><C-Down> <Cmd>call VSCodeNotify('workbench.action.moveEditorToBelowGroup')<CR>
nnoremap <C-w><C-Up> <Cmd>call VSCodeNotify('workbench.action.moveEditorToAboveGroup')<CR>
xnoremap <C-w><C-Up> <Cmd>call VSCodeNotify('workbench.action.moveEditorToAboveGroup')<CR>
nnoremap <C-w><C-Left> <Cmd>call VSCodeNotify('workbench.action.moveEditorToLeftGroup')<CR>
xnoremap <C-w><C-Left> <Cmd>call VSCodeNotify('workbench.action.moveEditorToLeftGroup')<CR>
nnoremap <C-w><C-Right> <Cmd>call VSCodeNotify('workbench.action.moveEditorToRightGroup')<CR>
xnoremap <C-w><C-Right> <Cmd>call VSCodeNotify('workbench.action.moveEditorToRightGroup')<CR>
nnoremap <C-w><S-j> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupDown')<CR>
xnoremap <C-w><S-j> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupDown')<CR>
nnoremap <C-w><S-k> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupUp')<CR>
xnoremap <C-w><S-k> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupUp')<CR>
nnoremap <C-w><S-h> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupLeft')<CR>
xnoremap <C-w><S-h> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupLeft')<CR>
nnoremap <C-w><S-l> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupRight')<CR>
xnoremap <C-w><S-l> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupRight')<CR>
nnoremap <C-w><S-Down> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupDown')<CR>
xnoremap <C-w><S-Down> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupDown')<CR>
nnoremap <C-w><S-Up> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupUp')<CR>
xnoremap <C-w><S-Up> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupUp')<CR>
nnoremap <C-w><S-Left> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupLeft')<CR>
xnoremap <C-w><S-Left> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupLeft')<CR>
nnoremap <C-w><S-Right> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupRight')<CR>
xnoremap <C-w><S-Right> <Cmd>call VSCodeNotify('workbench.action.moveActiveEditorGroupRight')<CR>
nnoremap <C-w>w <Cmd>call VSCodeNotify('workbench.action.focusNextGroup')<CR>
xnoremap <C-w>w <Cmd>call VSCodeNotify('workbench.action.focusNextGroup')<CR>
nnoremap <C-w><C-w> <Cmd>call VSCodeNotify('workbench.action.focusNextGroup')<CR>
xnoremap <C-w><C-w> <Cmd>call VSCodeNotify('workbench.action.focusNextGroup')<CR>
nnoremap <C-w>W <Cmd>call VSCodeNotify('workbench.action.focusPreviousGroup')<CR>
xnoremap <C-w>W <Cmd>call VSCodeNotify('workbench.action.focusPreviousGroup')<CR>
nnoremap <C-w>p <Cmd>call VSCodeNotify('workbench.action.focusPreviousGroup')<CR>
xnoremap <C-w>p <Cmd>call VSCodeNotify('workbench.action.focusPreviousGroup')<CR>
nnoremap <C-w>t <Cmd>call VSCodeNotify('workbench.action.focusFirstEditorGroup')<CR>
xnoremap <C-w>t <Cmd>call VSCodeNotify('workbench.action.focusFirstEditorGroup')<CR>
nnoremap <C-w>b <Cmd>call VSCodeNotify('workbench.action.focusLastEditorGroup')<CR>
xnoremap <C-w>b <Cmd>call VSCodeNotify('workbench.action.focusLastEditorGroup')<CR>

View File

@@ -0,0 +1,22 @@
local api = vim.api
local ns = api.nvim_create_namespace("vscode.statusline")
local curr_status = ""
local function refresh()
local status = ""
if vim.o.laststatus == 0 or vim.o.statusline == "" then
status = ""
else
local str = api.nvim_eval_statusline(vim.o.statusline, {}).str
status = str:gsub("\n", " "):gsub("%s+", " ")
end
if curr_status ~= status then
curr_status = status
vim.fn.VSCodeExtensionNotify("statusline", curr_status)
end
end
api.nvim_set_decoration_provider(ns, { on_end = refresh })

View File

@@ -0,0 +1,13 @@
-- No idea why this is necessary, but it is.
local api = vim.api
local util = require("vscode.util")
api.nvim_create_autocmd({ "CursorHold", "TextChanged", "InsertLeave" }, {
group = api.nvim_create_augroup("vscode.treesitter", {}),
callback = util.debounce(function()
pcall(function()
vim.treesitter.get_parser():parse()
end)
end, 100),
})

View File

@@ -0,0 +1,139 @@
local vscode = require("vscode")
--- Prompts the user to pick from a list of items, allowing arbitrary (potentially asynchronous)
--- work until `on_choice`.
---
--- Example:
---
--- ```lua
--- vim.ui.select({ 'tabs', 'spaces' }, {
--- prompt = 'Select tabs or spaces:',
--- format_item = function(item)
--- return "I'd like to choose " .. item
--- end,
--- }, function(choice)
--- if choice == 'spaces' then
--- vim.o.expandtab = true
--- else
--- vim.o.expandtab = false
--- end
--- end)
--- ```
---
---@param items table Arbitrary items
---@param opts table Additional options
--- - prompt (string|nil)
--- Text of the prompt. Defaults to `Select one of:`
--- - format_item (function item -> text)
--- Function to format an
--- individual item from `items`. Defaults to `tostring`.
--- - kind (string|nil)
--- Arbitrary hint string indicating the item shape.
--- Plugins reimplementing `vim.ui.select` may wish to
--- use this to infer the structure or semantics of
--- `items`, or the context in which select() was called.
---@param on_choice function ((item|nil, idx|nil) -> ())
--- Called once the user made a choice.
--- `idx` is the 1-based index of `item` within `items`.
--- `nil` if the user aborted the dialog.
local function vscode_ui_select(items, opts, on_choice)
-- validate parameters
vim.validate({
items = { items, "table", false },
on_choice = { on_choice, "function", false },
})
opts = opts or {}
-- set sane defaults
opts.prompt = opts.prompt or "Select one of"
opts.format_item = opts.format_item or tostring
opts.kind = opts.prompt or "string"
-- fill the vscode datastructures
local vscode_items = {}
for idx, item in ipairs(items) do
table.insert(vscode_items, {
idx = idx,
label = opts.format_item(item),
detail = item.detail or nil,
})
end
local vscode_opts = {
title = opts.prompt,
placeHolder = opts.prompt,
matchOnDetail = true,
}
-- open the select dialog
vscode.eval_async("return await vscode.window.showQuickPick(args.items, args.opts)", {
args = {
items = vscode_items,
opts = vscode_opts,
},
callback = function(err, res)
if err or res == vim.NIL then -- vim.NIL if cancelled
on_choice(nil, nil)
else
on_choice(items[res.idx], res.idx)
end
end,
})
end
--- Prompts the user for input, allowing arbitrary (potentially asynchronous) work until
--- `on_confirm`.
---
--- Example:
---
--- ```lua
--- vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
--- vim.o.shiftwidth = tonumber(input)
--- end)
--- ```
---
---@param opts table Additional options. See |input()|
--- - prompt (string|nil)
--- Text of the prompt
--- - default (string|nil)
--- Default reply to the input
---@param on_confirm function ((input|nil) -> ())
--- Called once the user confirms or abort the input.
--- `input` is what the user typed (it might be
--- an empty string if nothing was entered), or
--- `nil` if the user aborted the dialog.
local function vscode_ui_input(opts, on_confirm)
-- validate parameters
vim.validate({
on_confirm = { on_confirm, "function", false },
})
opts = opts or {}
-- set sane defaults
opts.prompt = opts.prompt or "Enter a value"
opts.default = opts.default or ""
-- fill the vscode datastructures
local vscode_opts = {
title = opts.prompt,
placeHolder = opts.prompt,
value = opts.default,
}
-- open the input dialog
vscode.eval_async("return await vscode.window.showInputBox(args.opts)", {
args = {
opts = vscode_opts,
},
callback = function(err, res)
if err or res == vim.NIL then -- vim.NIL if cancelled
on_confirm(nil)
else
on_confirm(res)
end
end,
})
end
-- remap the neovim ui function to use the vscode equivalents
vim.ui.select = vscode_ui_select
vim.ui.input = vscode_ui_input

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="coder-remote" Version="1.11.5" Publisher="coder" />
<DisplayName>Coder</DisplayName>
<Description xml:space="preserve">Open any workspace with a single click.</Description>
<Tags>remote-menu</Tags>
<Categories>Other</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.73.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="ms-vscode-remote.remote-ssh" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="ui" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Code.EnabledApiProposals" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExecutesCode" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/coder/vscode-coder.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/coder/vscode-coder.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/coder/vscode-coder.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/coder/vscode-coder/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/coder/vscode-coder#readme" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/media/logo.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/readme.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/changelog.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/media/logo.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@@ -0,0 +1,20 @@
The MIT License
Copyright (c) 2019 Coder Technologies Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,426 @@
# Change Log
## Unreleased
## [v1.11.5](https://github.com/coder/vscode-coder/releases/tag/v1.11.5) 2025-12-10
### Added
- Support for paths that begin with a tilde (`~`).
- Support for `coder ssh` flag configurations through the `coder.sshFlags` setting.
### Fixed
- Fixed race condition when multiple VS Code windows download the Coder CLI binary simultaneously.
Other windows now wait and display real-time progress instead of attempting concurrent downloads,
preventing corruption and failures.
- Remove duplicate "Cancel" buttons on the workspace update dialog.
### Changed
- WebSocket connections now automatically reconnect on network failures, improving reliability when
communicating with Coder deployments.
- Improved SSH process and log file discovery with better reconnect handling and support for
VS Code forks (Cursor, Windsurf, Antigravity).
## [v1.11.4](https://github.com/coder/vscode-coder/releases/tag/v1.11.4) 2025-11-20
### Added
- Support for the `google.antigravity-remote-openssh` Remote SSH extension.
### Changed
- Improved workspace connection progress messages and enhanced the workspace build terminal
with better log streaming. The extension now also waits for blocking startup scripts to
complete before connecting, providing clear progress indicators during the wait.
## [v1.11.3](https://github.com/coder/vscode-coder/releases/tag/v1.11.3) 2025-10-22
### Fixed
- Fixed WebSocket connections not receiving headers from the configured header command
(`coder.headerCommand`), which could cause authentication failures with remote workspaces.
## [v1.11.2](https://github.com/coder/vscode-coder/releases/tag/v1.11.2) 2025-10-07
### Changed
- Updated Visual Studio Marketplace badge in README to use img.shields.io service instead of vsmarketplacebadges.
## [v1.11.1](https://github.com/coder/vscode-coder/releases/tag/v1.11.1) 2025-10-07
### Fixed
- Logging in or out in one VS Code window now properly updates the authentication status in all other open windows.
- Fix an issue with JSON stringification errors occurring when logging circular objects.
- Fix resource cleanup issues that could leave lingering components after extension deactivation.
### Added
- Support for `CODER_BINARY_DESTINATION` environment variable to set CLI download location (overridden by extension setting `coder.binaryDestination` if configured).
- Search filter button to Coder Workspaces tree views for easier workspace discovery.
## [v1.11.0](https://github.com/coder/vscode-coder/releases/tag/v1.11.0) 2025-09-24
### Changed
- Always enable verbose (`-v`) flag when a log directory is configured (`coder.proxyLogDirectory`).
- Automatically start a workspace without prompting if it is explicitly opened but not running.
### Added
- Add support for CLI global flag configurations through the `coder.globalFlags` setting.
- Add logging for all REST traffic. Verbosity is configurable via `coder.httpClientLogLevel` (`none`, `basic`, `headers`, `body`).
- Add lifecycle logs for WebSocket creation, errors, and closures.
- Include UUIDs in REST and WebSocket logs to correlate events and measure duration.
## [1.10.1](https://github.com/coder/vscode-coder/releases/tag/v1.10.1) 2025-08-13
### Fixed
- The signature download fallback now uses only major.minor.patch without any
extra labels (like the hash), since the releases server does not include those
labels with its artifacts.
## [v1.10.0](https://github.com/coder/vscode-coder/releases/tag/v1.10.0) 2025-08-05
### Changed
- Coder output panel enhancements: all log entries now include timestamps, and
you can filter messages by log level in the panel.
### Added
- Update `/openDevContainer` to support all dev container features when hostPath
and configFile are provided.
- Add `coder.disableUpdateNotifications` setting to disable workspace template
update notifications.
- Consistently use the same session for each agent. Previously, depending on how
you connected, it could be possible to get two different sessions for an
agent. Existing connections may still have this problem; only new connections
are fixed.
- Add an agent metadata monitor status bar item, so you can view your active
agent metadata at a glance.
- Add binary signature verification. This can be disabled with
`coder.disableSignatureVerification` if you purposefully run a binary that is
not signed by Coder (for example a binary you built yourself).
## [v1.9.2](https://github.com/coder/vscode-coder/releases/tag/v1.9.2) 2025-06-25
### Fixed
- Use `--header-command` properly when starting a workspace.
- Handle `agent` parameter when opening workspace.
### Changed
- The Coder logo has been updated.
## [v1.9.1](https://github.com/coder/vscode-coder/releases/tag/v1.9.1) 2025-05-27
### Fixed
- Missing or otherwise malformed `START CODER VSCODE` / `END CODER VSCODE`
blocks in `${HOME}/.ssh/config` will now result in an error when attempting to
update the file. These will need to be manually fixed before proceeding.
- Multiple open instances of the extension could potentially clobber writes to
`~/.ssh/config`. Updates to this file are now atomic.
- Add support for `anysphere.remote-ssh` Remote SSH extension.
## [v1.9.0](https://github.com/coder/vscode-coder/releases/tag/v1.9.0) 2025-05-15
### Fixed
- The connection indicator will now show for VS Code on Windows, Windsurf, and
when using the `jeanp413.open-remote-ssh` extension.
### Changed
- The connection indicator now shows if connecting through Coder Desktop.
## [v1.8.0](https://github.com/coder/vscode-coder/releases/tag/v1.8.0) (2025-04-22)
### Added
- Coder extension sidebar now displays available app statuses, and lets
the user click them to drop into a session with a running AI Agent.
## [v1.7.1](https://github.com/coder/vscode-coder/releases/tag/v1.7.1) (2025-04-14)
### Fixed
- Fix bug where we were leaking SSE connections
## [v1.7.0](https://github.com/coder/vscode-coder/releases/tag/v1.7.0) (2025-04-03)
### Added
- Add new `/openDevContainer` path, similar to the `/open` path, except this
allows connecting to a dev container inside a workspace. For now, the dev
container must already be running for this to work.
### Fixed
- When not using token authentication, avoid setting `undefined` for the token
header, as Node will throw an error when headers are undefined. Now, we will
not set any header at all.
## [v1.6.0](https://github.com/coder/vscode-coder/releases/tag/v1.6.0) (2025-04-01)
### Added
- Add support for Coder inbox.
## [v1.5.0](https://github.com/coder/vscode-coder/releases/tag/v1.5.0) (2025-03-20)
### Fixed
- Fixed regression where autostart needed to be disabled.
### Changed
- Make the MS Remote SSH extension part of an extension pack rather than a hard dependency, to enable
using the plugin in other VSCode likes (cursor, windsurf, etc.)
## [v1.4.2](https://github.com/coder/vscode-coder/releases/tag/v1.4.2) (2025-03-07)
### Fixed
- Remove agent singleton so that client TLS certificates are reloaded on every API request.
- Use Axios client to receive event stream so TLS settings are properly applied.
- Set `usage-app=vscode` on `coder ssh` to fix deployment session counting.
- Fix version comparison logic for checking wildcard support in "coder ssh"
## [v1.4.1](https://github.com/coder/vscode-coder/releases/tag/v1.4.1) (2025-02-19)
### Fixed
- Recreate REST client in spots where confirmStart may have waited indefinitely.
## [v1.4.0](https://github.com/coder/vscode-coder/releases/tag/v1.4.0) (2025-02-04)
### Fixed
- Recreate REST client after starting a workspace to ensure fresh TLS certificates.
### Changed
- Use `coder ssh` subcommand in place of `coder vscodessh`.
## [v1.3.10](https://github.com/coder/vscode-coder/releases/tag/v1.3.10) (2025-01-17)
### Fixed
- Fix bug where checking for overridden properties incorrectly converted host name pattern to regular expression.
## [v1.3.9](https://github.com/coder/vscode-coder/releases/tag/v1.3.9) (2024-12-12)
### Fixed
- Only show a login failure dialog for explicit logins (and not autologins).
## [v1.3.8](https://github.com/coder/vscode-coder/releases/tag/v1.3.8) (2024-12-06)
### Changed
- When starting a workspace, shell out to the Coder binary instead of making an
API call. This reduces drift between what the plugin does and the CLI does. As
part of this, the `session_token` file was renamed to `session` since that is
what the CLI expects.
## [v1.3.7](https://github.com/coder/vscode-coder/releases/tag/v1.3.7) (2024-11-04)
### Added
- New setting `coder.tlsAltHost` to configure an alternative hostname to use for
TLS verification. This is useful when the hostname in the certificate does not
match the hostname used to connect.
## [v1.3.6](https://github.com/coder/vscode-coder/releases/tag/v1.3.6) (2024-11-04)
### Added
- Default URL setting that takes precedence over CODER_URL.
- Autologin setting that automatically initiates login when the extension
activates using either the default URL or CODER_URL.
### Changed
- When a client certificate and/or key is configured, skip token authentication.
## [v1.3.5](https://github.com/coder/vscode-coder/releases/tag/v1.3.5) (2024-10-16)
### Fixed
- Error messages from the workspace watch endpoint were not logged correctly.
- Delay notifying about workspaces shutting down since the connection might bump
the activity, making the notification misleading.
## [v1.3.4](https://github.com/coder/vscode-coder/releases/tag/v1.3.4) (2024-10-14)
### Fixed
- The "All Workspaces" view was not being populated due to visibility check.
### Added
- Log workspaces queries when running with `--log=debug`.
- Coder output logs will now have the date prefixed to each line.
## [v1.3.3](https://github.com/coder/vscode-coder/releases/tag/v1.3.3) (2024-10-14)
### Fixed
- The plugin no longer immediately starts polling workspaces when connecting to
a remote. It will only do this when the Coder sidebar is open.
### Changed
- Instead of monitoring all workspaces for impending autostops and deletions,
the plugin now only monitors the connected workspace.
## [v1.3.2](https://github.com/coder/vscode-coder/releases/tag/v1.3.2) (2024-09-10)
### Fixed
- Previously, if a workspace stopped or restarted causing the "Start" dialog to
appear in VS Code, the start button would fire a start workspace request
regardless of the workspace status.
Now we perform a check to see if the workspace is still stopped or failed. If
its status has changed out from under the IDE, it will not fire a redundant
start request.
- Fix a conflict with HTTP proxies and the library we use to make HTTP
requests. If you were getting 400 errors or similar from your proxy, please
try again.
### Changed
- Previously, the extension would always log SSH proxy diagnostics to a fixed
directory. Now this must be explicitly enabled by configuring a new setting
`coder.proxyLogDirectory`. If you are having connectivity issues, please
configure this setting and gather the logs before submitting an issue.
## [v1.3.1](https://github.com/coder/vscode-coder/releases/tag/v1.3.1) (2024-07-15)
### Fixed
- Avoid deleting the existing token when launching with a link that omits the
token.
## [v1.3.0](https://github.com/coder/vscode-coder/releases/tag/v1.3.0) (2024-07-01)
### Added
- If there are multiple agents, the plugin will now ask which to use.
### Fixed
- If the workspace is stopping as the plugin tries to connect, it will wait for
the stop and then try to start the workspace. Previously it would only start
the workspace if it happened to be in a fully stopped state when connecting.
- Whenever the plugin wants to start a workspace, it will ask the user first to
prevent constantly keeping a workspace up and defeating the point of
auto-stop.
## [v1.2.1](https://github.com/coder/vscode-coder/releases/tag/v1.2.1) (2024-06-25)
### Fixed
- Fix the update dialog continually reappearing.
## [v1.2.0](https://github.com/coder/vscode-coder/releases/tag/v1.2.0) (2024-06-21)
### Added
- New setting `coder.proxyBypass` which is the equivalent of `no_proxy`. This
only takes effect if `http.proxySupport` is `on` or `off`, otherwise VS Code
overrides the HTTP agent the plugin sets.
## [v1.1.0](https://github.com/coder/vscode-coder/releases/tag/v1.1.0) (2024-06-17)
### Added
- Workspace and agent statuses now show in the sidebar. These are updated every
five seconds.
- Support `http.proxy` setting and proxy environment variables. These only take
effect if `http.proxySupport` is `on` or `off`, otherwise VS Code overrides
the HTTP agent the plugin sets.
## [v1.0.2](https://github.com/coder/vscode-coder/releases/tag/v1.0.2) (2024-06-12)
### Fixed
- Redirects will now be followed when watching a workspace build, like when a
workspace is automatically started.
## [v1.0.1](https://github.com/coder/vscode-coder/releases/tag/v1.0.1) (2024-06-07)
### Changed
- Improve an error message for when watching a build fails.
## [v1.0.0](https://github.com/coder/vscode-coder/releases/tag/v1.0.0) (2024-06-05)
### Added
- Support opening workspaces that belong to a different deployment than the one
which is currently logged in. This will only work for new connections. If you
have an existing connection that errors when connecting because of this,
please connect it again using the plugin or the Coder dashboard. Optionally,
you may also want to delete your old workspaces from the recents list.
### Fixed
- Escape variables in the header command. If you have a variable in the header
command itself, like `echo TEST=$CODER_URL`, it will now work as expected
instead of being substituted with a blank or erroneous value.
## [v0.1.37](https://github.com/coder/vscode-coder/releases/tag/v0.1.37) (2024-05-24)
### Added
- openRecent query parameter to open the most recent workspace or directory for
that remote.
- Setting to disable downloading the binary. When disabled, the existing binary
will be used as-is. If the binary is missing, the plugin will error.
### Fixed
- Increased timeout will apply to reconnects as well.
### Changed
- Show certificate errors under the token input.
## [v0.1.36](https://github.com/coder/vscode-coder/releases/tag/v0.1.36) (2024-04-09)
### Changed
- Automatically update a workspace if required by the template.
- Show more information when remote setup fails.
### Fixed
- Abort remote connection when remote setup fails.
## [v0.1.35](https://github.com/coder/vscode-coder/releases/tag/v0.1.35) (2024-03-12)
### Changed
- Support running within Cursor.
## [v0.1.34](https://github.com/coder/vscode-coder/releases/tag/v0.1.34) (2024-03-03)
### Changed
- Improve fetching the Coder binary. This is mostly just better logging but it
also will avoid fetching if the existing binary version already matches, to
support scenarios where the ETag is ignored.
## [v0.1.33](https://github.com/coder/vscode-coder/releases/tag/v0.1.33) (2024-02-20)
### Fixed
- Prevent updating template when automatically starting workspace.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,112 @@
/*!
* Copyright (c) 2014, GMO GlobalSign
* Copyright (c) 2015-2022, Peculiar Ventures
* All rights reserved.
*
* Author 2014-2019, Yury Strozhevsky
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*!
* MIT License
*
* Copyright (c) Peculiar Ventures. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
/*!
* mime-db
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2015-2022 Douglas Christopher Wilson
* MIT Licensed
*/
/*!
* mime-types
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2015 Douglas Christopher Wilson
* MIT Licensed
*/
/*! *****************************************************************************
Copyright (C) Microsoft. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/*! Axios v1.12.2 Copyright (c) 2025 Matt Zabriskie and contributors */
/*! OpenPGP.js v6.2.2 - 2025-09-02 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 425.93 200">
<defs>
<style>
.cls-1 {
fill: #090b0b;
stroke-width: 0px;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<g>
<rect class="cls-1" x="263.75" y="5.41" width="162.18" height="189.24"/>
<path class="cls-1" d="M0,100C0,38.92,51.89,0,123.25,0s111.35,33.78,112.7,83.51l-61.62,1.89c-1.62-27.57-26.03-45.68-51.08-45.14-34.32.74-59.73,23.51-59.73,59.73s25.41,58.65,59.73,58.65c25.05,0,48.91-17.3,51.62-44.86l61.62,1.35c-1.62,50.54-44.05,84.87-113.24,84.87S0,160.81,0,100Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 425.93 200">
<defs>
<style>
.cls-1 {
fill: #fff;
stroke-width: 0px;
}
</style>
</defs>
<g id="Layer_2-2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1-2">
<g>
<rect class="cls-1" x="263.75" y="5.41" width="162.18" height="189.24"/>
<path class="cls-1" d="M0,100C0,38.92,51.89,0,123.25,0s111.35,33.78,112.7,83.51l-61.62,1.89c-1.62-27.57-26.03-45.68-51.08-45.14-34.32.74-59.73,23.51-59.73,59.73s25.41,58.65,59.73,58.65c25.05,0,48.91-17.3,51.62-44.86l61.62,1.35c-1.62,50.54-44.05,84.87-113.24,84.87S0,160.81,0,100Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,432 @@
{
"name": "coder-remote",
"displayName": "Coder",
"version": "1.11.5",
"description": "Open any workspace with a single click.",
"categories": [
"Other"
],
"bugs": {
"url": "https://github.com/coder/vscode-coder/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coder/vscode-coder"
},
"license": "MIT",
"publisher": "coder",
"type": "commonjs",
"main": "./dist/extension.js",
"scripts": {
"build": "webpack",
"fmt": "prettier --write .",
"lint": "eslint . --ext ts,md,json",
"lint:fix": "yarn lint --fix",
"package": "webpack --mode production --devtool hidden-source-map",
"package:prerelease": "npx vsce package --pre-release",
"pretest": "tsc -p . --outDir out && tsc -p test --outDir out && yarn run build && yarn run lint",
"test": "ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs",
"test:ci": "CI=true yarn test",
"test:integration": "vscode-test",
"vscode:prepublish": "yarn package",
"watch": "webpack --watch"
},
"contributes": {
"configuration": {
"title": "Coder",
"properties": {
"coder.sshConfig": {
"markdownDescription": "These values will be included in the ssh config file. Eg: `'ConnectTimeout=10'` will set the timeout to 10 seconds. Any values included here will override anything provided by default or by the deployment. To unset a value that is written by default, set the value to the empty string, Eg: `'ConnectTimeout='` will unset it.",
"type": "array",
"items": {
"title": "SSH Config Value",
"type": "string",
"pattern": "^[a-zA-Z0-9-]+[=\\s].*$"
},
"scope": "machine"
},
"coder.insecure": {
"markdownDescription": "If true, the extension will not verify the authenticity of the remote host. This is useful for self-signed certificates.",
"type": "boolean",
"default": false
},
"coder.binarySource": {
"markdownDescription": "Used to download the Coder CLI which is necessary to make SSH connections. The If-None-Match header will be set to the SHA1 of the CLI and can be used for caching. Absolute URLs will be used as-is; otherwise this value will be resolved against the deployment domain. Defaults to downloading from the Coder deployment.",
"type": "string",
"default": ""
},
"coder.binaryDestination": {
"markdownDescription": "The full path of the directory into which the Coder CLI will be downloaded. Defaults to the value of `CODER_BINARY_DESTINATION` if not set, otherwise the extension's global storage directory.",
"type": "string",
"default": ""
},
"coder.enableDownloads": {
"markdownDescription": "Allow the plugin to download the CLI when missing or out of date.",
"type": "boolean",
"default": true
},
"coder.headerCommand": {
"markdownDescription": "An external command that outputs additional HTTP headers added to all requests. The command must output each header as `key=value` on its own line. The following environment variables will be available to the process: `CODER_URL`. Defaults to the value of `CODER_HEADER_COMMAND` if not set.",
"type": "string",
"default": ""
},
"coder.tlsCertFile": {
"markdownDescription": "Path to file for TLS client cert. When specified, token authorization will be skipped. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsKeyFile": {
"markdownDescription": "Path to file for TLS client key. When specified, token authorization will be skipped. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsCaFile": {
"markdownDescription": "Path to file for TLS certificate authority. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsAltHost": {
"markdownDescription": "Alternative hostname to use for TLS verification. This is useful when the hostname in the certificate does not match the hostname used to connect.",
"type": "string",
"default": ""
},
"coder.proxyLogDirectory": {
"markdownDescription": "If set, the Coder CLI will output extra SSH information into this directory, which can be helpful for debugging connectivity issues.",
"type": "string",
"default": ""
},
"coder.proxyBypass": {
"markdownDescription": "If not set, will inherit from the `no_proxy` or `NO_PROXY` environment variables. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.defaultUrl": {
"markdownDescription": "This will be shown in the URL prompt, along with the CODER_URL environment variable if set, for the user to select when logging in.",
"type": "string",
"default": ""
},
"coder.autologin": {
"markdownDescription": "Automatically log into the default URL when the extension is activated. coder.defaultUrl is preferred, otherwise the CODER_URL environment variable will be used. This setting has no effect if neither is set.",
"type": "boolean",
"default": false
},
"coder.disableUpdateNotifications": {
"markdownDescription": "Disable notifications when workspace template updates are available.",
"type": "boolean",
"default": false
},
"coder.disableSignatureVerification": {
"markdownDescription": "Disable Coder CLI signature verification, which can be useful if you run an unsigned fork of the binary.",
"type": "boolean",
"default": false
},
"coder.sshFlags": {
"markdownDescription": "Additional flags to pass to the `coder ssh` command when establishing SSH connections. Enter each flag as a separate array item; values are passed verbatim and in order. See the [CLI ssh reference](https://coder.com/docs/reference/cli/ssh) for available flags.\n\nNote: `--network-info-dir` and `--ssh-host-prefix` are ignored (managed internally). Prefer `#coder.proxyLogDirectory#` over `--log-dir`/`-l` for full functionality.",
"type": "array",
"items": {
"type": "string"
},
"default": [
"--disable-autostart"
]
},
"coder.globalFlags": {
"markdownDescription": "Global flags to pass to every Coder CLI invocation. Enter each flag as a separate array item; values are passed verbatim and in order. Do **not** include the `coder` command itself. See the [CLI reference](https://coder.com/docs/reference/cli) for available global flags.\n\nNote that for `--header-command`, precedence is: `#coder.headerCommand#` setting, then `CODER_HEADER_COMMAND` environment variable, then the value specified here. The `--global-config` flag is explicitly ignored.",
"type": "array",
"items": {
"type": "string"
}
},
"coder.httpClientLogLevel": {
"markdownDescription": "Controls the verbosity of HTTP client logging. This affects what details are logged for each HTTP request and response.",
"type": "string",
"enum": [
"none",
"basic",
"headers",
"body"
],
"markdownEnumDescriptions": [
"Disables all HTTP client logging",
"Logs the request method, URL, length, and the response status code",
"Logs everything from *basic* plus sanitized request and response headers",
"Logs everything from *headers* plus request and response bodies (may include sensitive data)"
],
"default": "basic"
}
}
},
"viewsContainers": {
"activitybar": [
{
"id": "coder",
"title": "Coder Remote",
"icon": "media/logo-white.svg"
}
]
},
"views": {
"coder": [
{
"id": "myWorkspaces",
"name": "My Workspaces",
"visibility": "visible",
"icon": "media/logo-white.svg"
},
{
"id": "allWorkspaces",
"name": "All Workspaces",
"visibility": "visible",
"icon": "media/logo-white.svg",
"when": "coder.authenticated && coder.isOwner"
}
]
},
"viewsWelcome": [
{
"view": "myWorkspaces",
"contents": "Coder is a platform that provisions remote development environments. \n[Login](command:coder.login)",
"when": "!coder.authenticated && coder.loaded"
}
],
"commands": [
{
"command": "coder.login",
"title": "Coder: Login"
},
{
"command": "coder.logout",
"title": "Coder: Logout",
"when": "coder.authenticated",
"icon": "$(sign-out)"
},
{
"command": "coder.open",
"title": "Open Workspace",
"icon": "$(play)",
"category": "Coder"
},
{
"command": "coder.openFromSidebar",
"title": "Coder: Open Workspace",
"icon": "$(play)"
},
{
"command": "coder.createWorkspace",
"title": "Create Workspace",
"category": "Coder",
"when": "coder.authenticated",
"icon": "$(add)"
},
{
"command": "coder.navigateToWorkspace",
"title": "Navigate to Workspace Page",
"when": "coder.authenticated",
"icon": "$(link-external)"
},
{
"command": "coder.navigateToWorkspaceSettings",
"title": "Edit Workspace Settings",
"when": "coder.authenticated",
"icon": "$(settings-gear)"
},
{
"command": "coder.workspace.update",
"title": "Coder: Update Workspace",
"when": "coder.workspace.updatable"
},
{
"command": "coder.refreshWorkspaces",
"title": "Refresh Workspace",
"category": "Coder",
"icon": "$(refresh)",
"when": "coder.authenticated"
},
{
"command": "coder.viewLogs",
"title": "Coder: View Logs",
"icon": "$(list-unordered)",
"when": "coder.authenticated"
},
{
"command": "coder.openAppStatus",
"title": "Coder: Open App Status",
"icon": "$(robot)",
"when": "coder.authenticated"
},
{
"command": "coder.searchMyWorkspaces",
"title": "Search",
"category": "Coder",
"icon": "$(search)"
},
{
"command": "coder.searchAllWorkspaces",
"title": "Search",
"category": "Coder",
"icon": "$(search)"
}
],
"menus": {
"commandPalette": [
{
"command": "coder.openFromSidebar",
"when": "false"
},
{
"command": "coder.searchMyWorkspaces",
"when": "false"
},
{
"command": "coder.searchAllWorkspaces",
"when": "false"
}
],
"view/title": [
{
"command": "coder.logout",
"when": "coder.authenticated && view == myWorkspaces"
},
{
"command": "coder.login",
"when": "!coder.authenticated && view == myWorkspaces"
},
{
"command": "coder.createWorkspace",
"when": "coder.authenticated && view == myWorkspaces",
"group": "navigation@1"
},
{
"command": "coder.refreshWorkspaces",
"when": "coder.authenticated && view == myWorkspaces",
"group": "navigation@2"
},
{
"command": "coder.searchMyWorkspaces",
"when": "coder.authenticated && view == myWorkspaces",
"group": "navigation@3"
},
{
"command": "coder.searchAllWorkspaces",
"when": "coder.authenticated && view == allWorkspaces",
"group": "navigation@3"
}
],
"view/item/context": [
{
"command": "coder.openFromSidebar",
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderAgent",
"group": "inline"
},
{
"command": "coder.navigateToWorkspace",
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderWorkspaceMultipleAgents",
"group": "inline"
},
{
"command": "coder.navigateToWorkspaceSettings",
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderWorkspaceMultipleAgents",
"group": "inline"
}
],
"statusBar/remoteIndicator": [
{
"command": "coder.open",
"group": "remote_11_ssh_coder@1"
},
{
"command": "coder.createWorkspace",
"group": "remote_11_ssh_coder@2",
"when": "coder.authenticated"
}
]
}
},
"activationEvents": [
"onResolveRemoteAuthority:ssh-remote",
"onCommand:coder.connect",
"onUri"
],
"resolutions": {
"semver": "7.7.3",
"trim": "0.0.3",
"word-wrap": "1.2.5"
},
"dependencies": {
"@peculiar/x509": "^1.14.0",
"axios": "1.12.2",
"date-fns": "^3.6.0",
"eventsource": "^3.0.6",
"find-process": "^2.0.0",
"jsonc-parser": "^3.3.1",
"openpgp": "^6.2.2",
"pretty-bytes": "^7.1.0",
"proper-lockfile": "^4.1.2",
"proxy-agent": "^6.5.0",
"semver": "^7.7.3",
"ua-parser-js": "1.0.40",
"ws": "^8.18.3",
"zod": "^4.1.12"
},
"devDependencies": {
"@types/eventsource": "^3.0.0",
"@types/glob": "^7.1.3",
"@types/node": "^22.14.1",
"@types/proper-lockfile": "^4.1.4",
"@types/semver": "^7.7.1",
"@types/ua-parser-js": "0.7.36",
"@types/vscode": "^1.73.0",
"@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^8.44.0",
"@typescript-eslint/parser": "^8.46.4",
"@vitest/coverage-v8": "^3.2.4",
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.7.1",
"bufferutil": "^4.0.9",
"coder": "https://github.com/coder/coder#main",
"dayjs": "^1.11.19",
"electron": "^39.2.6",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-md": "^1.0.19",
"eslint-plugin-package-json": "^0.59.0",
"eslint-plugin-prettier": "^5.5.4",
"glob": "^11.1.0",
"jsonc-eslint-parser": "^2.4.0",
"markdown-eslint-parser": "^1.2.1",
"memfs": "^4.49.0",
"nyc": "^17.1.0",
"prettier": "^3.6.2",
"ts-loader": "^9.5.4",
"typescript": "^5.9.3",
"utf-8-validate": "^6.0.5",
"vitest": "^3.2.4",
"vscode-test": "^1.5.0",
"webpack": "^5.101.3",
"webpack-cli": "^6.0.1"
},
"extensionPack": [
"ms-vscode-remote.remote-ssh"
],
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
"engines": {
"vscode": "^1.73.0"
},
"icon": "media/logo.png",
"extensionKind": [
"ui"
],
"capabilities": {
"untrustedWorkspaces": {
"supported": true
}
},
"__metadata": {
"installedTimestamp": 1765760155823,
"targetPlatform": "undefined",
"size": 2866196
}
}

View File

@@ -0,0 +1,99 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGPGrCwBEAC7SSKQIFoQdt3jYv/1okRdoleepLDG4NfcG52S45Ex3/fUA6Z/
ewHQrx//SN+h1FLpb0zQMyamWrSh2O3dnkWridwlskb5/y8C/6OUdk4L/ZgHeyPO
Ncbyl1hqO8oViakiWt4IxwSYo83eJHxOUiCGZlqV6EpEsaur43BRHnK8EciNeIxF
Bjle3yXH1K3EgGGHpgnSoKe1nSVxtWIwX45d06v+VqnBoI6AyK0Zp+Nn8bL0EnXC
xGYU3XOkC6EmITlhMju1AhxnbkQiy8IUxXiaj3NoPc1khapOcyBybhESjRZHlgu4
ToLZGaypjtfQJgMeFlpua7sJK0ziFMW4wOTX+6Ix/S6XA80dVbl3VEhSMpFCcgI+
OmEd2JuBs6maG+92fCRIzGAClzV8/ifM//JU9D7Qlq6QJpcbNClODlPNDNe7RUEO
b7Bu7dJJS3VhHO9eEen6m6vRE4DNriHT4Zvq1UkHfpJUW7njzkIYRni3eNrsr4Da
U/eeGbVipok4lzZEOQtuaZlX9ytOdGrWEGMGSosTOG6u6KAKJoz7cQGZiz4pZpjR
3N2SIYv59lgpHrIV7UodGx9nzu0EKBhkoulaP1UzH8F16psSaJXRjeyl/YP8Rd2z
SYgZVLjTzkTUXkJT8fQO8zLBEuwA0IiXX5Dl7grfEeShANVrM9LVu8KkUwARAQAB
tC5Db2RlciBSZWxlYXNlIFNpZ25pbmcgS2V5IDxzZWN1cml0eUBjb2Rlci5jb20+
iQJUBBMBCgA+FiEEKMY4lDj2Q3PIwvSKi87Yfbu4ZEsFAmPGrCwCGwMFCQWjmoAF
CwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQi87Yfbu4ZEvrQQ//a3ySdMVhnLP+
KneonV2zuNilTMC2J/MNG7Q0hU+8I9bxCc6DDqcnBBCQkIUwJq3wmelt3nTC8RxI
fv+ggnbdF9pz7Fc91nIJsGlWpH+bu1tSIvKF/rzZA8v6xUblFFfaC7Gsc5P4xk/+
h0XBDAy6K+7+AafgLFpRD08Y0Kf2aMcqdM6c2Zo4IPo6FNrOa66FNkypZdQ4IByW
4kMezZSTp4Phqd9yqGC4m44U8YgzmW9LHgrvS0JyIaRPcQFM31AJ50K3iYRxL1ll
ETqJvbDR8UORNQs3Qs3CEZL588BoDMX2TYObTCG6g9Om5vJT0kgUkjDxQHwbAj6E
z9j8BoWkDT2JNzwdfTbPueuRjO+A+TXA9XZtrzbEYEzh0sD9Bdr7ozSF3JAs4GZS
nqcVlyp7q44ZdePR9L8w0ksth56tBWHfE9hi5jbRDRY2OnkV7y7JtWnBDQx9bCIo
7L7aBT8eirI1ZOnUxHJrnqY5matfWjSDBFW+YmWUkjnzBsa9F4m8jq9MSD3Q/8hN
ksJFrmLQs0/8hnM39tS7kLnAaWeGvbmjnxdeMqZsICxNpbyQrq2AhF4GhWfc+NsZ
yznVagJZ9bIlGsycSXJbsA5GbXDnm172TlodMUbLF9FU8i0vV4Y7q6jKO/VsblKU
F0bhXIRqVLrd9g88IyVyyZozmwbJKIy5Ag0EY8asLAEQAMgI9bMurq6Zic4s5W0u
W6LBDHyZhe+w2a3oT/i2YgTsh8XmIjrNasYYWO67b50JKepA3fk3ZA44w8WJqq+z
HLpslEb2fY5I1HvENUMKjYAUIsswSC21DSBau4yYiRGF0MNqv/MWy5Rjc993vIU4
4TM3mvVhPrYfIkr0jwSbxq8+cm3sBjr0gcBQO57C3w8QkcZ6jefuI7y+1ZeM7X3L
OngmBFJDEutd9LPO/6Is4j/iQfTb8WDR6OmMX3Y04RHrP4sm7jf+3ZZKjcFCZQjr
QA4XHcQyJjnMN34Fn1U7KWopivU+mqViAnVpA643dq9SiBqsl83/R03DrpwKpP7r
6qasUHSUULuS7A4n8+CDwK5KghvrS0hOwMiYoIwZIVPITSUFHPYxrCJK7gU2OHfk
IZHX5m9L5iNwLz958GwzwHuONs5bjMxILbKknRhEBOcbhcpk0jswiPNUrEdipRZY
GR9G9fzD6q4P5heV3kQRqyUUTxdDj8w7jbrwl8sm5zk+TMnPRsu2kg0uwIN1aILm
oVkDN5CiZtg00n2Fu3do5F3YkF0Cz7indx5yySr5iUuoCY0EnpqSwourJ/ZdZA9Y
ZCHjhgjwyPCbxpTGfLj1g25jzQBYn5Wdgr2aHCQcqnU8DKPCnYL9COHJJylgj0vN
NSxyDjNXYYwSrYMqs/91f5xVABEBAAGJAjwEGAEKACYWIQQoxjiUOPZDc8jC9IqL
zth9u7hkSwUCY8asLAIbDAUJBaOagAAKCRCLzth9u7hkSyMvD/0Qal5kwiKDjgBr
i/dtMka+WNBTMb6vKoM759o33YAl22On5WgLr9Uz0cjkJPtzMHxhUo8KQmiPRtsK
dOmG9NI9NttfSeQVbeL8V/DC672fWPKM4TB8X7Kkj56/KI7ueGRokDhXG2pJlhQr
HwzZsAKoCMMnjcquAhHJClK9heIpVLBGFVlmVzJETzxo6fbEU/c7L79+hOrR4BWx
Tg6Dk7mbAGe7BuQLNtw6gcWUVWtHS4iYQtE/4khU1QppC1Z/ZbZ+AJT2TAFXzIaw
0l9tcOh7+TXqsvCLsXN0wrUh1nOdxA81sNWEMY07bG1qgvHyVc7ZYM89/ApK2HP+
bBDIpAsRCGu2MHtrnJIlNE1J14G1mnauR5qIqI3C0R5MPLXOcDtp+gnjFe+PLU+6
rQxJObyOkyEpOvtVtJKfFnpI5bqyl8WEPN0rDaS2A27cGXi5nynSAqoM1xT15W21
uyY2GXY26DIwVfc59wGeclwcM29nS7prRU3KtskjonJ0iQoQebYOHLxy896cK+pK
nnhZx5AQjYiZPsPktSNZjSuOvTZ3g+IDwbCSvmBHcQpitzUOPShTUTs0QjSttzk2
I6WxP9ivoR9yJGsxwNgCgrYdyt5+hyXXW/aUVihnQwizQRbymjJ2/z+I8NRFIeYb
xbtNFaH3WjLnhm9CB/H+Lc8fUj6HaZkCDQRjxt6QARAAsjZuCMjZBaAC1LFMeRcv
9+Ck7T5UNXTL9xQr1jUFZR95I6loWiWvFJ3Uet7gIbgNYY5Dc1gDr1Oqx9KQBjsN
TUahXov5lmjF5mYeyWTDZ5TS8H3o50zQzfZRC1eEbqjiBMLAHv74KD13P62nvzv6
Dejwc7Nwc6aOH3cdZm74kz4EmdobJYRVdd5X9EYH/hdM928SsipKhm44oj3RDGi/
x+ptjW9gr0bnrgCbkyCMNKhnmHSM60I8f4/viRItb+hWRpZYfLxMGTBVunicSXcX
Zh6Fq/DD/yTjzN9N83/NdDvwCyKo5U/kPgD2Ixh5PyJ38cpz6774Awnb/tstCI1g
glnlNbu8Qz84STr3NRZMOgT5h5b5qASOeruG4aVo9euaYJHlnlgcoUmpbEMnwr0L
tREUXSHGXWor7EYPjUQLskIaPl9NCZ3MEw5LhsZTgEdFBnb54dxMSEl7/MYDYhD/
uTIWOJmtsWHmuMmvfxnw5GDEhJnAp4dxUm9BZlJhfnVR07DtTKyEk37+kl6+i0ZQ
yU4HJ2GWItpLfK54E/CH+S91y7wpepb2TMkaFR2fCK0vXTGAXWK+Y+aTD8ZcLB5y
0IYPsvA0by5AFpmXNfWZiZtYvgJ5FAQZNuB5RILg3HsuDq2U4wzp5BoohWtsOzsn
antIUf/bN0D2g+pCySkc5ssAEQEAAbQuQ29kZXIgUmVsZWFzZSBTaWduaW5nIEtl
eSA8c2VjdXJpdHlAY29kZXIuY29tPokCVAQTAQoAPhYhBCHJaxy5UHGIdPZNvWpa
ZxteQKO5BQJjxt6QAhsDBQkFo5qABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJ
EGpaZxteQKO5oysP/1rSdvbKMzozvnVZoglnPjnSGStY9Pr2ziGL7eIMk2yt+Orr
j/AwxYIDgsZPQoJEr87eX2dCYtUMM1x+CpZsWu8dDVFLxyZp8nPmhUzcUCFfutw1
UmAVKQkOra9segZtw4HVcSctpdgLw7NHq7vIQm4knIvjWmdC15r1B6/VJJI8CeaR
Zy+ToPr9fKnYs1RNdz+DRDN2521skX1DaInhB/ALeid90rJTRujaP9XeyNb9k32K
qd3h4C0KUGIf0fNKj4mmDlNosX3V/pJZATpFiF8aVPlybHQ2W5xpn1U8FJxE4hgR
rvsZmO685Qwm6p/uRI5Eymfm8JC5OQNt9Kvs/BMhotsW0u+je8UXwnznptMILpVP
+qxNuHUe1MYLdjK21LFF+Pk5O4W1TT6mKcbisOmZuQMG5DxpzUwm1Rs5AX1omuJt
iOrmQEvmrKKWC9qbcmWW1t2scnIJsNtrsvME0UjJFz+RL6UUX3xXlLK6YOUghCr8
gZ7ZPgFqygS6tMu8TAGURzSCfijDh+eZGwqrlvngBIaO5WiNdSXC/J9aE1KThXmX
90A3Gwry+yI2kRS7o8vmghXewPTZbnG0CVHiQIH2yqFNXnhKvhaJt0g04TcnxBte
kiFqRT4K1Bb7pUIlUANmrKo9/zRCxIOopEgRH5cVQ8ZglkT0t5d3ePmAo6h0uQIN
BGPG3pABEADghhNByVoC+qCMo+SErjxz9QYA+tKoAngbgPyxxyB4RD52Z58MwVaP
+Yk0qxJYUBat3dJwiCTlUGG+yTyMOwLl7qSDr53AD5ml0hwJqnLBJ6OUyGE4ax4D
RUVBprKlDltwr98cZDgzvwEhIO2T3tNZ4vySveITj9pLonOrLkAfGXqFOqom+S37
6eZvjKTnEUbT+S0TTynwds70W31sxVUrL62qsUnmoKEnsKXk/7X8CLXWvtNqu9kf
eiXs5Jz4N6RZUqvS0WOaaWG9v1PHukTtb8RyeookhsBqf9fWOlw5foel+NQwGQjz
0D0dDTKxn2Taweq+gWNCRH7/FJNdWa9upZ2fUAjg9hN9Ow8Y5nE3J0YKCBAQTgNa
XNtsiGQjdEKYZslxZKFM34By3LD6IrkcAEPKu9plZthmqhQumqwYRAgB9O56jg3N
GDDRyAMS7y63nNphTSatpOZtPVVMtcBw5jPjMIPFfU2dlfsvmnCvru2dvfAij+Ng
EkwOLNS8rFQHMJSQysmHuAPSYT97Yl022mPrAtb9+hwtCXt3VI6dvIARl2qPyF0D
DMw2fW5E7ivhUr2WEFiBmXunrJvMIYldBzDkkBjamelPjoevR0wfoIn0x1CbSsQi
zbEs3PXHs7nGxb9TZnHY4+J94mYHdSXrImAuH/x97OnlfUpOKPv5lwARAQABiQI8
BBgBCgAmFiEEIclrHLlQcYh09k29alpnG15Ao7kFAmPG3pACGwwFCQWjmoAACgkQ
alpnG15Ao7m2/g//Y/YRM+Qhf71G0MJpAfym6ZqmwsT78qQ8T9w95ZeIRD7UUE8d
tm39kqJTGP6DuHCNYEMs2M88o0SoQsS/7j/8is7H/13F5o40DWjuQphia2BWkB1B
G4QRRIXMlrPX8PS92GDCtGfvxn90Li2FhQGZWlNFwvKUB7+/yLMsZzOwo7BS6PwC
hvI3eC7DBC8sXjJUxsrgFAkxQxSx/njP8f4HdUwhNnB1YA2/5IY5bk8QrXxzrAK1
sbIAjpJdtPYOrZByyyj4ZpRcSm3ngV2n8yd1muJ5u+oRIQoGCdEIaweCj598jNFa
k378ZA11hCyNFHjpPIKnF3tfsQ8vjDatoq4Asy+HXFuo1GA/lvNgNb3Nv4FUozuv
JYJ0KaW73FZXlFBIBkMkRQE8TspHy2v/IGyNXBwKncmkszaiiozBd+T+1NUZgtk5
9o5uKQwLHVnHIU7r/w/oN5LvLawLg2dP/f2u/KoQXMxjwLZncSH4+5tRz4oa/GMn
k4F84AxTIjGfLJeXigyP6xIPQbvJy+8iLRaCpj+v/EPwAedbRV+u0JFeqqikca70
aGN86JBOmwpU87sfFxLI7HdI02DkvlxYYK3vYlA6zEyWaeLZ3VNr6tHcQmOnFe8Q
26gcS0AQcxQZrcWTCZ8DJYF+RnXjSVRmHV/3YDts4JyMKcD6QX8s/3aaldk=
=dLmT
-----END PGP PUBLIC KEY BLOCK-----

View File

@@ -0,0 +1,44 @@
# Coder Remote
[![Visual Studio Marketplace](https://img.shields.io/visual-studio-marketplace/v/coder.coder-remote?label=Visual%20Studio%20Marketplace&color=%233fba11)](https://marketplace.visualstudio.com/items?itemName=coder.coder-remote)
[![Open VSX Version](https://img.shields.io/open-vsx/v/coder/coder-remote)](https://open-vsx.org/extension/coder/coder-remote)
[!["Join us on
Discord"](https://badgen.net/discord/online-members/coder)](https://coder.com/chat?utm_source=github.com/coder/vscode-coder&utm_medium=github&utm_campaign=readme.md)
The Coder Remote extension lets you open [Coder](https://github.com/coder/coder)
workspaces with a single click.
- Open workspaces from the dashboard in a single click.
- Automatically start workspaces when opened.
- No command-line or local dependencies required - just install your editor!
- Works in air-gapped or restricted networks. Just connect to your Coder
deployment!
- Supports multiple editors: VS Code, Cursor, and Windsurf.
> [!NOTE]
> The extension builds on VS Code-provided implementations of SSH. Make
> sure you have the correct SSH extension installed for your editor
> (`ms-vscode-remote.remote-ssh` or `codeium.windsurf-remote-openssh` for Windsurf).
![Demo](https://github.com/coder/vscode-coder/raw/main/demo.gif?raw=true)
## Getting Started
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press
enter.
```shell
ext install coder.coder-remote
```
Alternatively, manually install the VSIX from the
[latest release](https://github.com/coder/vscode-coder/releases/latest).
### Variables Reference
Coder uses `${userHome}` from VS Code's
[variables reference](https://code.visualstudio.com/docs/editor/variables-reference).
Use this when formatting paths in the Coder extension settings rather than `~`
or `$HOME`.
Example: ${userHome}/foo/bar.baz

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="coder-remote" Version="1.11.6" Publisher="coder" />
<DisplayName>Coder</DisplayName>
<Description xml:space="preserve">Open any workspace with a single click.</Description>
<Tags>remote-menu</Tags>
<Categories>Other</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.73.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="ms-vscode-remote.remote-ssh" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="ui" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Code.EnabledApiProposals" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExecutesCode" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/coder/vscode-coder.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/coder/vscode-coder.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/coder/vscode-coder.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/coder/vscode-coder/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/coder/vscode-coder#readme" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/media/logo.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/readme.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/changelog.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/media/logo.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@@ -0,0 +1,20 @@
The MIT License
Copyright (c) 2019 Coder Technologies Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,436 @@
# Change Log
## Unreleased
## [v1.11.6](https://github.com/coder/vscode-coder/releases/tag/v1.11.6) 2025-12-15
### Added
- Log file picker when viewing logs without an active workspace connection.
### Fixed
- Fixed false "setting changed" notifications appearing when connecting to a remote workspace.
## [v1.11.5](https://github.com/coder/vscode-coder/releases/tag/v1.11.5) 2025-12-10
### Added
- Support for paths that begin with a tilde (`~`).
- Support for `coder ssh` flag configurations through the `coder.sshFlags` setting.
### Fixed
- Fixed race condition when multiple VS Code windows download the Coder CLI binary simultaneously.
Other windows now wait and display real-time progress instead of attempting concurrent downloads,
preventing corruption and failures.
- Remove duplicate "Cancel" buttons on the workspace update dialog.
### Changed
- WebSocket connections now automatically reconnect on network failures, improving reliability when
communicating with Coder deployments.
- Improved SSH process and log file discovery with better reconnect handling and support for
VS Code forks (Cursor, Windsurf, Antigravity).
## [v1.11.4](https://github.com/coder/vscode-coder/releases/tag/v1.11.4) 2025-11-20
### Added
- Support for the `google.antigravity-remote-openssh` Remote SSH extension.
### Changed
- Improved workspace connection progress messages and enhanced the workspace build terminal
with better log streaming. The extension now also waits for blocking startup scripts to
complete before connecting, providing clear progress indicators during the wait.
## [v1.11.3](https://github.com/coder/vscode-coder/releases/tag/v1.11.3) 2025-10-22
### Fixed
- Fixed WebSocket connections not receiving headers from the configured header command
(`coder.headerCommand`), which could cause authentication failures with remote workspaces.
## [v1.11.2](https://github.com/coder/vscode-coder/releases/tag/v1.11.2) 2025-10-07
### Changed
- Updated Visual Studio Marketplace badge in README to use img.shields.io service instead of vsmarketplacebadges.
## [v1.11.1](https://github.com/coder/vscode-coder/releases/tag/v1.11.1) 2025-10-07
### Fixed
- Logging in or out in one VS Code window now properly updates the authentication status in all other open windows.
- Fix an issue with JSON stringification errors occurring when logging circular objects.
- Fix resource cleanup issues that could leave lingering components after extension deactivation.
### Added
- Support for `CODER_BINARY_DESTINATION` environment variable to set CLI download location (overridden by extension setting `coder.binaryDestination` if configured).
- Search filter button to Coder Workspaces tree views for easier workspace discovery.
## [v1.11.0](https://github.com/coder/vscode-coder/releases/tag/v1.11.0) 2025-09-24
### Changed
- Always enable verbose (`-v`) flag when a log directory is configured (`coder.proxyLogDirectory`).
- Automatically start a workspace without prompting if it is explicitly opened but not running.
### Added
- Add support for CLI global flag configurations through the `coder.globalFlags` setting.
- Add logging for all REST traffic. Verbosity is configurable via `coder.httpClientLogLevel` (`none`, `basic`, `headers`, `body`).
- Add lifecycle logs for WebSocket creation, errors, and closures.
- Include UUIDs in REST and WebSocket logs to correlate events and measure duration.
## [1.10.1](https://github.com/coder/vscode-coder/releases/tag/v1.10.1) 2025-08-13
### Fixed
- The signature download fallback now uses only major.minor.patch without any
extra labels (like the hash), since the releases server does not include those
labels with its artifacts.
## [v1.10.0](https://github.com/coder/vscode-coder/releases/tag/v1.10.0) 2025-08-05
### Changed
- Coder output panel enhancements: all log entries now include timestamps, and
you can filter messages by log level in the panel.
### Added
- Update `/openDevContainer` to support all dev container features when hostPath
and configFile are provided.
- Add `coder.disableUpdateNotifications` setting to disable workspace template
update notifications.
- Consistently use the same session for each agent. Previously, depending on how
you connected, it could be possible to get two different sessions for an
agent. Existing connections may still have this problem; only new connections
are fixed.
- Add an agent metadata monitor status bar item, so you can view your active
agent metadata at a glance.
- Add binary signature verification. This can be disabled with
`coder.disableSignatureVerification` if you purposefully run a binary that is
not signed by Coder (for example a binary you built yourself).
## [v1.9.2](https://github.com/coder/vscode-coder/releases/tag/v1.9.2) 2025-06-25
### Fixed
- Use `--header-command` properly when starting a workspace.
- Handle `agent` parameter when opening workspace.
### Changed
- The Coder logo has been updated.
## [v1.9.1](https://github.com/coder/vscode-coder/releases/tag/v1.9.1) 2025-05-27
### Fixed
- Missing or otherwise malformed `START CODER VSCODE` / `END CODER VSCODE`
blocks in `${HOME}/.ssh/config` will now result in an error when attempting to
update the file. These will need to be manually fixed before proceeding.
- Multiple open instances of the extension could potentially clobber writes to
`~/.ssh/config`. Updates to this file are now atomic.
- Add support for `anysphere.remote-ssh` Remote SSH extension.
## [v1.9.0](https://github.com/coder/vscode-coder/releases/tag/v1.9.0) 2025-05-15
### Fixed
- The connection indicator will now show for VS Code on Windows, Windsurf, and
when using the `jeanp413.open-remote-ssh` extension.
### Changed
- The connection indicator now shows if connecting through Coder Desktop.
## [v1.8.0](https://github.com/coder/vscode-coder/releases/tag/v1.8.0) (2025-04-22)
### Added
- Coder extension sidebar now displays available app statuses, and lets
the user click them to drop into a session with a running AI Agent.
## [v1.7.1](https://github.com/coder/vscode-coder/releases/tag/v1.7.1) (2025-04-14)
### Fixed
- Fix bug where we were leaking SSE connections
## [v1.7.0](https://github.com/coder/vscode-coder/releases/tag/v1.7.0) (2025-04-03)
### Added
- Add new `/openDevContainer` path, similar to the `/open` path, except this
allows connecting to a dev container inside a workspace. For now, the dev
container must already be running for this to work.
### Fixed
- When not using token authentication, avoid setting `undefined` for the token
header, as Node will throw an error when headers are undefined. Now, we will
not set any header at all.
## [v1.6.0](https://github.com/coder/vscode-coder/releases/tag/v1.6.0) (2025-04-01)
### Added
- Add support for Coder inbox.
## [v1.5.0](https://github.com/coder/vscode-coder/releases/tag/v1.5.0) (2025-03-20)
### Fixed
- Fixed regression where autostart needed to be disabled.
### Changed
- Make the MS Remote SSH extension part of an extension pack rather than a hard dependency, to enable
using the plugin in other VSCode likes (cursor, windsurf, etc.)
## [v1.4.2](https://github.com/coder/vscode-coder/releases/tag/v1.4.2) (2025-03-07)
### Fixed
- Remove agent singleton so that client TLS certificates are reloaded on every API request.
- Use Axios client to receive event stream so TLS settings are properly applied.
- Set `usage-app=vscode` on `coder ssh` to fix deployment session counting.
- Fix version comparison logic for checking wildcard support in "coder ssh"
## [v1.4.1](https://github.com/coder/vscode-coder/releases/tag/v1.4.1) (2025-02-19)
### Fixed
- Recreate REST client in spots where confirmStart may have waited indefinitely.
## [v1.4.0](https://github.com/coder/vscode-coder/releases/tag/v1.4.0) (2025-02-04)
### Fixed
- Recreate REST client after starting a workspace to ensure fresh TLS certificates.
### Changed
- Use `coder ssh` subcommand in place of `coder vscodessh`.
## [v1.3.10](https://github.com/coder/vscode-coder/releases/tag/v1.3.10) (2025-01-17)
### Fixed
- Fix bug where checking for overridden properties incorrectly converted host name pattern to regular expression.
## [v1.3.9](https://github.com/coder/vscode-coder/releases/tag/v1.3.9) (2024-12-12)
### Fixed
- Only show a login failure dialog for explicit logins (and not autologins).
## [v1.3.8](https://github.com/coder/vscode-coder/releases/tag/v1.3.8) (2024-12-06)
### Changed
- When starting a workspace, shell out to the Coder binary instead of making an
API call. This reduces drift between what the plugin does and the CLI does. As
part of this, the `session_token` file was renamed to `session` since that is
what the CLI expects.
## [v1.3.7](https://github.com/coder/vscode-coder/releases/tag/v1.3.7) (2024-11-04)
### Added
- New setting `coder.tlsAltHost` to configure an alternative hostname to use for
TLS verification. This is useful when the hostname in the certificate does not
match the hostname used to connect.
## [v1.3.6](https://github.com/coder/vscode-coder/releases/tag/v1.3.6) (2024-11-04)
### Added
- Default URL setting that takes precedence over CODER_URL.
- Autologin setting that automatically initiates login when the extension
activates using either the default URL or CODER_URL.
### Changed
- When a client certificate and/or key is configured, skip token authentication.
## [v1.3.5](https://github.com/coder/vscode-coder/releases/tag/v1.3.5) (2024-10-16)
### Fixed
- Error messages from the workspace watch endpoint were not logged correctly.
- Delay notifying about workspaces shutting down since the connection might bump
the activity, making the notification misleading.
## [v1.3.4](https://github.com/coder/vscode-coder/releases/tag/v1.3.4) (2024-10-14)
### Fixed
- The "All Workspaces" view was not being populated due to visibility check.
### Added
- Log workspaces queries when running with `--log=debug`.
- Coder output logs will now have the date prefixed to each line.
## [v1.3.3](https://github.com/coder/vscode-coder/releases/tag/v1.3.3) (2024-10-14)
### Fixed
- The plugin no longer immediately starts polling workspaces when connecting to
a remote. It will only do this when the Coder sidebar is open.
### Changed
- Instead of monitoring all workspaces for impending autostops and deletions,
the plugin now only monitors the connected workspace.
## [v1.3.2](https://github.com/coder/vscode-coder/releases/tag/v1.3.2) (2024-09-10)
### Fixed
- Previously, if a workspace stopped or restarted causing the "Start" dialog to
appear in VS Code, the start button would fire a start workspace request
regardless of the workspace status.
Now we perform a check to see if the workspace is still stopped or failed. If
its status has changed out from under the IDE, it will not fire a redundant
start request.
- Fix a conflict with HTTP proxies and the library we use to make HTTP
requests. If you were getting 400 errors or similar from your proxy, please
try again.
### Changed
- Previously, the extension would always log SSH proxy diagnostics to a fixed
directory. Now this must be explicitly enabled by configuring a new setting
`coder.proxyLogDirectory`. If you are having connectivity issues, please
configure this setting and gather the logs before submitting an issue.
## [v1.3.1](https://github.com/coder/vscode-coder/releases/tag/v1.3.1) (2024-07-15)
### Fixed
- Avoid deleting the existing token when launching with a link that omits the
token.
## [v1.3.0](https://github.com/coder/vscode-coder/releases/tag/v1.3.0) (2024-07-01)
### Added
- If there are multiple agents, the plugin will now ask which to use.
### Fixed
- If the workspace is stopping as the plugin tries to connect, it will wait for
the stop and then try to start the workspace. Previously it would only start
the workspace if it happened to be in a fully stopped state when connecting.
- Whenever the plugin wants to start a workspace, it will ask the user first to
prevent constantly keeping a workspace up and defeating the point of
auto-stop.
## [v1.2.1](https://github.com/coder/vscode-coder/releases/tag/v1.2.1) (2024-06-25)
### Fixed
- Fix the update dialog continually reappearing.
## [v1.2.0](https://github.com/coder/vscode-coder/releases/tag/v1.2.0) (2024-06-21)
### Added
- New setting `coder.proxyBypass` which is the equivalent of `no_proxy`. This
only takes effect if `http.proxySupport` is `on` or `off`, otherwise VS Code
overrides the HTTP agent the plugin sets.
## [v1.1.0](https://github.com/coder/vscode-coder/releases/tag/v1.1.0) (2024-06-17)
### Added
- Workspace and agent statuses now show in the sidebar. These are updated every
five seconds.
- Support `http.proxy` setting and proxy environment variables. These only take
effect if `http.proxySupport` is `on` or `off`, otherwise VS Code overrides
the HTTP agent the plugin sets.
## [v1.0.2](https://github.com/coder/vscode-coder/releases/tag/v1.0.2) (2024-06-12)
### Fixed
- Redirects will now be followed when watching a workspace build, like when a
workspace is automatically started.
## [v1.0.1](https://github.com/coder/vscode-coder/releases/tag/v1.0.1) (2024-06-07)
### Changed
- Improve an error message for when watching a build fails.
## [v1.0.0](https://github.com/coder/vscode-coder/releases/tag/v1.0.0) (2024-06-05)
### Added
- Support opening workspaces that belong to a different deployment than the one
which is currently logged in. This will only work for new connections. If you
have an existing connection that errors when connecting because of this,
please connect it again using the plugin or the Coder dashboard. Optionally,
you may also want to delete your old workspaces from the recents list.
### Fixed
- Escape variables in the header command. If you have a variable in the header
command itself, like `echo TEST=$CODER_URL`, it will now work as expected
instead of being substituted with a blank or erroneous value.
## [v0.1.37](https://github.com/coder/vscode-coder/releases/tag/v0.1.37) (2024-05-24)
### Added
- openRecent query parameter to open the most recent workspace or directory for
that remote.
- Setting to disable downloading the binary. When disabled, the existing binary
will be used as-is. If the binary is missing, the plugin will error.
### Fixed
- Increased timeout will apply to reconnects as well.
### Changed
- Show certificate errors under the token input.
## [v0.1.36](https://github.com/coder/vscode-coder/releases/tag/v0.1.36) (2024-04-09)
### Changed
- Automatically update a workspace if required by the template.
- Show more information when remote setup fails.
### Fixed
- Abort remote connection when remote setup fails.
## [v0.1.35](https://github.com/coder/vscode-coder/releases/tag/v0.1.35) (2024-03-12)
### Changed
- Support running within Cursor.
## [v0.1.34](https://github.com/coder/vscode-coder/releases/tag/v0.1.34) (2024-03-03)
### Changed
- Improve fetching the Coder binary. This is mostly just better logging but it
also will avoid fetching if the existing binary version already matches, to
support scenarios where the ETag is ignored.
## [v0.1.33](https://github.com/coder/vscode-coder/releases/tag/v0.1.33) (2024-02-20)
### Fixed
- Prevent updating template when automatically starting workspace.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,112 @@
/*!
* Copyright (c) 2014, GMO GlobalSign
* Copyright (c) 2015-2022, Peculiar Ventures
* All rights reserved.
*
* Author 2014-2019, Yury Strozhevsky
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*!
* MIT License
*
* Copyright (c) Peculiar Ventures. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
/*!
* mime-db
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2015-2022 Douglas Christopher Wilson
* MIT Licensed
*/
/*!
* mime-types
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2015 Douglas Christopher Wilson
* MIT Licensed
*/
/*! *****************************************************************************
Copyright (C) Microsoft. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/*! Axios v1.12.2 Copyright (c) 2025 Matt Zabriskie and contributors */
/*! OpenPGP.js v6.2.2 - 2025-09-02 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 425.93 200">
<defs>
<style>
.cls-1 {
fill: #090b0b;
stroke-width: 0px;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<g>
<rect class="cls-1" x="263.75" y="5.41" width="162.18" height="189.24"/>
<path class="cls-1" d="M0,100C0,38.92,51.89,0,123.25,0s111.35,33.78,112.7,83.51l-61.62,1.89c-1.62-27.57-26.03-45.68-51.08-45.14-34.32.74-59.73,23.51-59.73,59.73s25.41,58.65,59.73,58.65c25.05,0,48.91-17.3,51.62-44.86l61.62,1.35c-1.62,50.54-44.05,84.87-113.24,84.87S0,160.81,0,100Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 425.93 200">
<defs>
<style>
.cls-1 {
fill: #fff;
stroke-width: 0px;
}
</style>
</defs>
<g id="Layer_2-2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1-2">
<g>
<rect class="cls-1" x="263.75" y="5.41" width="162.18" height="189.24"/>
<path class="cls-1" d="M0,100C0,38.92,51.89,0,123.25,0s111.35,33.78,112.7,83.51l-61.62,1.89c-1.62-27.57-26.03-45.68-51.08-45.14-34.32.74-59.73,23.51-59.73,59.73s25.41,58.65,59.73,58.65c25.05,0,48.91-17.3,51.62-44.86l61.62,1.35c-1.62,50.54-44.05,84.87-113.24,84.87S0,160.81,0,100Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,432 @@
{
"name": "coder-remote",
"displayName": "Coder",
"version": "1.11.6",
"description": "Open any workspace with a single click.",
"categories": [
"Other"
],
"bugs": {
"url": "https://github.com/coder/vscode-coder/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coder/vscode-coder"
},
"license": "MIT",
"publisher": "coder",
"type": "commonjs",
"main": "./dist/extension.js",
"scripts": {
"build": "webpack",
"fmt": "prettier --write .",
"lint": "eslint . --ext ts,md,json",
"lint:fix": "yarn lint --fix",
"package": "webpack --mode production --devtool hidden-source-map",
"package:prerelease": "npx vsce package --pre-release",
"pretest": "tsc -p . --outDir out && tsc -p test --outDir out && yarn run build && yarn run lint",
"test": "ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs",
"test:ci": "CI=true yarn test",
"test:integration": "vscode-test",
"vscode:prepublish": "yarn package",
"watch": "webpack --watch"
},
"contributes": {
"configuration": {
"title": "Coder",
"properties": {
"coder.sshConfig": {
"markdownDescription": "These values will be included in the ssh config file. Eg: `'ConnectTimeout=10'` will set the timeout to 10 seconds. Any values included here will override anything provided by default or by the deployment. To unset a value that is written by default, set the value to the empty string, Eg: `'ConnectTimeout='` will unset it.",
"type": "array",
"items": {
"title": "SSH Config Value",
"type": "string",
"pattern": "^[a-zA-Z0-9-]+[=\\s].*$"
},
"scope": "machine"
},
"coder.insecure": {
"markdownDescription": "If true, the extension will not verify the authenticity of the remote host. This is useful for self-signed certificates.",
"type": "boolean",
"default": false
},
"coder.binarySource": {
"markdownDescription": "Used to download the Coder CLI which is necessary to make SSH connections. The If-None-Match header will be set to the SHA1 of the CLI and can be used for caching. Absolute URLs will be used as-is; otherwise this value will be resolved against the deployment domain. Defaults to downloading from the Coder deployment.",
"type": "string",
"default": ""
},
"coder.binaryDestination": {
"markdownDescription": "The full path of the directory into which the Coder CLI will be downloaded. Defaults to the value of `CODER_BINARY_DESTINATION` if not set, otherwise the extension's global storage directory.",
"type": "string",
"default": ""
},
"coder.enableDownloads": {
"markdownDescription": "Allow the plugin to download the CLI when missing or out of date.",
"type": "boolean",
"default": true
},
"coder.headerCommand": {
"markdownDescription": "An external command that outputs additional HTTP headers added to all requests. The command must output each header as `key=value` on its own line. The following environment variables will be available to the process: `CODER_URL`. Defaults to the value of `CODER_HEADER_COMMAND` if not set.",
"type": "string",
"default": ""
},
"coder.tlsCertFile": {
"markdownDescription": "Path to file for TLS client cert. When specified, token authorization will be skipped. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsKeyFile": {
"markdownDescription": "Path to file for TLS client key. When specified, token authorization will be skipped. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsCaFile": {
"markdownDescription": "Path to file for TLS certificate authority. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsAltHost": {
"markdownDescription": "Alternative hostname to use for TLS verification. This is useful when the hostname in the certificate does not match the hostname used to connect.",
"type": "string",
"default": ""
},
"coder.proxyLogDirectory": {
"markdownDescription": "If set, the Coder CLI will output extra SSH information into this directory, which can be helpful for debugging connectivity issues.",
"type": "string",
"default": ""
},
"coder.proxyBypass": {
"markdownDescription": "If not set, will inherit from the `no_proxy` or `NO_PROXY` environment variables. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.defaultUrl": {
"markdownDescription": "This will be shown in the URL prompt, along with the CODER_URL environment variable if set, for the user to select when logging in.",
"type": "string",
"default": ""
},
"coder.autologin": {
"markdownDescription": "Automatically log into the default URL when the extension is activated. coder.defaultUrl is preferred, otherwise the CODER_URL environment variable will be used. This setting has no effect if neither is set.",
"type": "boolean",
"default": false
},
"coder.disableUpdateNotifications": {
"markdownDescription": "Disable notifications when workspace template updates are available.",
"type": "boolean",
"default": false
},
"coder.disableSignatureVerification": {
"markdownDescription": "Disable Coder CLI signature verification, which can be useful if you run an unsigned fork of the binary.",
"type": "boolean",
"default": false
},
"coder.sshFlags": {
"markdownDescription": "Additional flags to pass to the `coder ssh` command when establishing SSH connections. Enter each flag as a separate array item; values are passed verbatim and in order. See the [CLI ssh reference](https://coder.com/docs/reference/cli/ssh) for available flags.\n\nNote: `--network-info-dir` and `--ssh-host-prefix` are ignored (managed internally). Prefer `#coder.proxyLogDirectory#` over `--log-dir`/`-l` for full functionality.",
"type": "array",
"items": {
"type": "string"
},
"default": [
"--disable-autostart"
]
},
"coder.globalFlags": {
"markdownDescription": "Global flags to pass to every Coder CLI invocation. Enter each flag as a separate array item; values are passed verbatim and in order. Do **not** include the `coder` command itself. See the [CLI reference](https://coder.com/docs/reference/cli) for available global flags.\n\nNote that for `--header-command`, precedence is: `#coder.headerCommand#` setting, then `CODER_HEADER_COMMAND` environment variable, then the value specified here. The `--global-config` flag is explicitly ignored.",
"type": "array",
"items": {
"type": "string"
}
},
"coder.httpClientLogLevel": {
"markdownDescription": "Controls the verbosity of HTTP client logging. This affects what details are logged for each HTTP request and response.",
"type": "string",
"enum": [
"none",
"basic",
"headers",
"body"
],
"markdownEnumDescriptions": [
"Disables all HTTP client logging",
"Logs the request method, URL, length, and the response status code",
"Logs everything from *basic* plus sanitized request and response headers",
"Logs everything from *headers* plus request and response bodies (may include sensitive data)"
],
"default": "basic"
}
}
},
"viewsContainers": {
"activitybar": [
{
"id": "coder",
"title": "Coder Remote",
"icon": "media/logo-white.svg"
}
]
},
"views": {
"coder": [
{
"id": "myWorkspaces",
"name": "My Workspaces",
"visibility": "visible",
"icon": "media/logo-white.svg"
},
{
"id": "allWorkspaces",
"name": "All Workspaces",
"visibility": "visible",
"icon": "media/logo-white.svg",
"when": "coder.authenticated && coder.isOwner"
}
]
},
"viewsWelcome": [
{
"view": "myWorkspaces",
"contents": "Coder is a platform that provisions remote development environments. \n[Login](command:coder.login)",
"when": "!coder.authenticated && coder.loaded"
}
],
"commands": [
{
"command": "coder.login",
"title": "Coder: Login"
},
{
"command": "coder.logout",
"title": "Coder: Logout",
"when": "coder.authenticated",
"icon": "$(sign-out)"
},
{
"command": "coder.open",
"title": "Open Workspace",
"icon": "$(play)",
"category": "Coder"
},
{
"command": "coder.openFromSidebar",
"title": "Coder: Open Workspace",
"icon": "$(play)"
},
{
"command": "coder.createWorkspace",
"title": "Create Workspace",
"category": "Coder",
"when": "coder.authenticated",
"icon": "$(add)"
},
{
"command": "coder.navigateToWorkspace",
"title": "Navigate to Workspace Page",
"when": "coder.authenticated",
"icon": "$(link-external)"
},
{
"command": "coder.navigateToWorkspaceSettings",
"title": "Edit Workspace Settings",
"when": "coder.authenticated",
"icon": "$(settings-gear)"
},
{
"command": "coder.workspace.update",
"title": "Coder: Update Workspace",
"when": "coder.workspace.updatable"
},
{
"command": "coder.refreshWorkspaces",
"title": "Refresh Workspace",
"category": "Coder",
"icon": "$(refresh)",
"when": "coder.authenticated"
},
{
"command": "coder.viewLogs",
"title": "Coder: View Logs",
"icon": "$(list-unordered)",
"when": "coder.authenticated"
},
{
"command": "coder.openAppStatus",
"title": "Coder: Open App Status",
"icon": "$(robot)",
"when": "coder.authenticated"
},
{
"command": "coder.searchMyWorkspaces",
"title": "Search",
"category": "Coder",
"icon": "$(search)"
},
{
"command": "coder.searchAllWorkspaces",
"title": "Search",
"category": "Coder",
"icon": "$(search)"
}
],
"menus": {
"commandPalette": [
{
"command": "coder.openFromSidebar",
"when": "false"
},
{
"command": "coder.searchMyWorkspaces",
"when": "false"
},
{
"command": "coder.searchAllWorkspaces",
"when": "false"
}
],
"view/title": [
{
"command": "coder.logout",
"when": "coder.authenticated && view == myWorkspaces"
},
{
"command": "coder.login",
"when": "!coder.authenticated && view == myWorkspaces"
},
{
"command": "coder.createWorkspace",
"when": "coder.authenticated && view == myWorkspaces",
"group": "navigation@1"
},
{
"command": "coder.refreshWorkspaces",
"when": "coder.authenticated && view == myWorkspaces",
"group": "navigation@2"
},
{
"command": "coder.searchMyWorkspaces",
"when": "coder.authenticated && view == myWorkspaces",
"group": "navigation@3"
},
{
"command": "coder.searchAllWorkspaces",
"when": "coder.authenticated && view == allWorkspaces",
"group": "navigation@3"
}
],
"view/item/context": [
{
"command": "coder.openFromSidebar",
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderAgent",
"group": "inline"
},
{
"command": "coder.navigateToWorkspace",
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderWorkspaceMultipleAgents",
"group": "inline"
},
{
"command": "coder.navigateToWorkspaceSettings",
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderWorkspaceMultipleAgents",
"group": "inline"
}
],
"statusBar/remoteIndicator": [
{
"command": "coder.open",
"group": "remote_11_ssh_coder@1"
},
{
"command": "coder.createWorkspace",
"group": "remote_11_ssh_coder@2",
"when": "coder.authenticated"
}
]
}
},
"activationEvents": [
"onResolveRemoteAuthority:ssh-remote",
"onCommand:coder.connect",
"onUri"
],
"resolutions": {
"semver": "7.7.3",
"trim": "0.0.3",
"word-wrap": "1.2.5"
},
"dependencies": {
"@peculiar/x509": "^1.14.0",
"axios": "1.12.2",
"date-fns": "^3.6.0",
"eventsource": "^3.0.6",
"find-process": "^2.0.0",
"jsonc-parser": "^3.3.1",
"openpgp": "^6.2.2",
"pretty-bytes": "^7.1.0",
"proper-lockfile": "^4.1.2",
"proxy-agent": "^6.5.0",
"semver": "^7.7.3",
"ua-parser-js": "1.0.40",
"ws": "^8.18.3",
"zod": "^4.1.12"
},
"devDependencies": {
"@types/eventsource": "^3.0.0",
"@types/glob": "^7.1.3",
"@types/node": "^22.14.1",
"@types/proper-lockfile": "^4.1.4",
"@types/semver": "^7.7.1",
"@types/ua-parser-js": "0.7.36",
"@types/vscode": "^1.73.0",
"@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^8.44.0",
"@typescript-eslint/parser": "^8.46.4",
"@vitest/coverage-v8": "^3.2.4",
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.7.1",
"bufferutil": "^4.0.9",
"coder": "https://github.com/coder/coder#main",
"dayjs": "^1.11.19",
"electron": "^39.2.6",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-md": "^1.0.19",
"eslint-plugin-package-json": "^0.59.0",
"eslint-plugin-prettier": "^5.5.4",
"glob": "^11.1.0",
"jsonc-eslint-parser": "^2.4.0",
"markdown-eslint-parser": "^1.2.1",
"memfs": "^4.49.0",
"nyc": "^17.1.0",
"prettier": "^3.6.2",
"ts-loader": "^9.5.4",
"typescript": "^5.9.3",
"utf-8-validate": "^6.0.5",
"vitest": "^3.2.4",
"vscode-test": "^1.5.0",
"webpack": "^5.101.3",
"webpack-cli": "^6.0.1"
},
"extensionPack": [
"ms-vscode-remote.remote-ssh"
],
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
"engines": {
"vscode": "^1.73.0"
},
"icon": "media/logo.png",
"extensionKind": [
"ui"
],
"capabilities": {
"untrustedWorkspaces": {
"supported": true
}
},
"__metadata": {
"installedTimestamp": 1765806966741,
"targetPlatform": "undefined",
"size": 2867581
}
}

View File

@@ -0,0 +1,99 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGPGrCwBEAC7SSKQIFoQdt3jYv/1okRdoleepLDG4NfcG52S45Ex3/fUA6Z/
ewHQrx//SN+h1FLpb0zQMyamWrSh2O3dnkWridwlskb5/y8C/6OUdk4L/ZgHeyPO
Ncbyl1hqO8oViakiWt4IxwSYo83eJHxOUiCGZlqV6EpEsaur43BRHnK8EciNeIxF
Bjle3yXH1K3EgGGHpgnSoKe1nSVxtWIwX45d06v+VqnBoI6AyK0Zp+Nn8bL0EnXC
xGYU3XOkC6EmITlhMju1AhxnbkQiy8IUxXiaj3NoPc1khapOcyBybhESjRZHlgu4
ToLZGaypjtfQJgMeFlpua7sJK0ziFMW4wOTX+6Ix/S6XA80dVbl3VEhSMpFCcgI+
OmEd2JuBs6maG+92fCRIzGAClzV8/ifM//JU9D7Qlq6QJpcbNClODlPNDNe7RUEO
b7Bu7dJJS3VhHO9eEen6m6vRE4DNriHT4Zvq1UkHfpJUW7njzkIYRni3eNrsr4Da
U/eeGbVipok4lzZEOQtuaZlX9ytOdGrWEGMGSosTOG6u6KAKJoz7cQGZiz4pZpjR
3N2SIYv59lgpHrIV7UodGx9nzu0EKBhkoulaP1UzH8F16psSaJXRjeyl/YP8Rd2z
SYgZVLjTzkTUXkJT8fQO8zLBEuwA0IiXX5Dl7grfEeShANVrM9LVu8KkUwARAQAB
tC5Db2RlciBSZWxlYXNlIFNpZ25pbmcgS2V5IDxzZWN1cml0eUBjb2Rlci5jb20+
iQJUBBMBCgA+FiEEKMY4lDj2Q3PIwvSKi87Yfbu4ZEsFAmPGrCwCGwMFCQWjmoAF
CwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQi87Yfbu4ZEvrQQ//a3ySdMVhnLP+
KneonV2zuNilTMC2J/MNG7Q0hU+8I9bxCc6DDqcnBBCQkIUwJq3wmelt3nTC8RxI
fv+ggnbdF9pz7Fc91nIJsGlWpH+bu1tSIvKF/rzZA8v6xUblFFfaC7Gsc5P4xk/+
h0XBDAy6K+7+AafgLFpRD08Y0Kf2aMcqdM6c2Zo4IPo6FNrOa66FNkypZdQ4IByW
4kMezZSTp4Phqd9yqGC4m44U8YgzmW9LHgrvS0JyIaRPcQFM31AJ50K3iYRxL1ll
ETqJvbDR8UORNQs3Qs3CEZL588BoDMX2TYObTCG6g9Om5vJT0kgUkjDxQHwbAj6E
z9j8BoWkDT2JNzwdfTbPueuRjO+A+TXA9XZtrzbEYEzh0sD9Bdr7ozSF3JAs4GZS
nqcVlyp7q44ZdePR9L8w0ksth56tBWHfE9hi5jbRDRY2OnkV7y7JtWnBDQx9bCIo
7L7aBT8eirI1ZOnUxHJrnqY5matfWjSDBFW+YmWUkjnzBsa9F4m8jq9MSD3Q/8hN
ksJFrmLQs0/8hnM39tS7kLnAaWeGvbmjnxdeMqZsICxNpbyQrq2AhF4GhWfc+NsZ
yznVagJZ9bIlGsycSXJbsA5GbXDnm172TlodMUbLF9FU8i0vV4Y7q6jKO/VsblKU
F0bhXIRqVLrd9g88IyVyyZozmwbJKIy5Ag0EY8asLAEQAMgI9bMurq6Zic4s5W0u
W6LBDHyZhe+w2a3oT/i2YgTsh8XmIjrNasYYWO67b50JKepA3fk3ZA44w8WJqq+z
HLpslEb2fY5I1HvENUMKjYAUIsswSC21DSBau4yYiRGF0MNqv/MWy5Rjc993vIU4
4TM3mvVhPrYfIkr0jwSbxq8+cm3sBjr0gcBQO57C3w8QkcZ6jefuI7y+1ZeM7X3L
OngmBFJDEutd9LPO/6Is4j/iQfTb8WDR6OmMX3Y04RHrP4sm7jf+3ZZKjcFCZQjr
QA4XHcQyJjnMN34Fn1U7KWopivU+mqViAnVpA643dq9SiBqsl83/R03DrpwKpP7r
6qasUHSUULuS7A4n8+CDwK5KghvrS0hOwMiYoIwZIVPITSUFHPYxrCJK7gU2OHfk
IZHX5m9L5iNwLz958GwzwHuONs5bjMxILbKknRhEBOcbhcpk0jswiPNUrEdipRZY
GR9G9fzD6q4P5heV3kQRqyUUTxdDj8w7jbrwl8sm5zk+TMnPRsu2kg0uwIN1aILm
oVkDN5CiZtg00n2Fu3do5F3YkF0Cz7indx5yySr5iUuoCY0EnpqSwourJ/ZdZA9Y
ZCHjhgjwyPCbxpTGfLj1g25jzQBYn5Wdgr2aHCQcqnU8DKPCnYL9COHJJylgj0vN
NSxyDjNXYYwSrYMqs/91f5xVABEBAAGJAjwEGAEKACYWIQQoxjiUOPZDc8jC9IqL
zth9u7hkSwUCY8asLAIbDAUJBaOagAAKCRCLzth9u7hkSyMvD/0Qal5kwiKDjgBr
i/dtMka+WNBTMb6vKoM759o33YAl22On5WgLr9Uz0cjkJPtzMHxhUo8KQmiPRtsK
dOmG9NI9NttfSeQVbeL8V/DC672fWPKM4TB8X7Kkj56/KI7ueGRokDhXG2pJlhQr
HwzZsAKoCMMnjcquAhHJClK9heIpVLBGFVlmVzJETzxo6fbEU/c7L79+hOrR4BWx
Tg6Dk7mbAGe7BuQLNtw6gcWUVWtHS4iYQtE/4khU1QppC1Z/ZbZ+AJT2TAFXzIaw
0l9tcOh7+TXqsvCLsXN0wrUh1nOdxA81sNWEMY07bG1qgvHyVc7ZYM89/ApK2HP+
bBDIpAsRCGu2MHtrnJIlNE1J14G1mnauR5qIqI3C0R5MPLXOcDtp+gnjFe+PLU+6
rQxJObyOkyEpOvtVtJKfFnpI5bqyl8WEPN0rDaS2A27cGXi5nynSAqoM1xT15W21
uyY2GXY26DIwVfc59wGeclwcM29nS7prRU3KtskjonJ0iQoQebYOHLxy896cK+pK
nnhZx5AQjYiZPsPktSNZjSuOvTZ3g+IDwbCSvmBHcQpitzUOPShTUTs0QjSttzk2
I6WxP9ivoR9yJGsxwNgCgrYdyt5+hyXXW/aUVihnQwizQRbymjJ2/z+I8NRFIeYb
xbtNFaH3WjLnhm9CB/H+Lc8fUj6HaZkCDQRjxt6QARAAsjZuCMjZBaAC1LFMeRcv
9+Ck7T5UNXTL9xQr1jUFZR95I6loWiWvFJ3Uet7gIbgNYY5Dc1gDr1Oqx9KQBjsN
TUahXov5lmjF5mYeyWTDZ5TS8H3o50zQzfZRC1eEbqjiBMLAHv74KD13P62nvzv6
Dejwc7Nwc6aOH3cdZm74kz4EmdobJYRVdd5X9EYH/hdM928SsipKhm44oj3RDGi/
x+ptjW9gr0bnrgCbkyCMNKhnmHSM60I8f4/viRItb+hWRpZYfLxMGTBVunicSXcX
Zh6Fq/DD/yTjzN9N83/NdDvwCyKo5U/kPgD2Ixh5PyJ38cpz6774Awnb/tstCI1g
glnlNbu8Qz84STr3NRZMOgT5h5b5qASOeruG4aVo9euaYJHlnlgcoUmpbEMnwr0L
tREUXSHGXWor7EYPjUQLskIaPl9NCZ3MEw5LhsZTgEdFBnb54dxMSEl7/MYDYhD/
uTIWOJmtsWHmuMmvfxnw5GDEhJnAp4dxUm9BZlJhfnVR07DtTKyEk37+kl6+i0ZQ
yU4HJ2GWItpLfK54E/CH+S91y7wpepb2TMkaFR2fCK0vXTGAXWK+Y+aTD8ZcLB5y
0IYPsvA0by5AFpmXNfWZiZtYvgJ5FAQZNuB5RILg3HsuDq2U4wzp5BoohWtsOzsn
antIUf/bN0D2g+pCySkc5ssAEQEAAbQuQ29kZXIgUmVsZWFzZSBTaWduaW5nIEtl
eSA8c2VjdXJpdHlAY29kZXIuY29tPokCVAQTAQoAPhYhBCHJaxy5UHGIdPZNvWpa
ZxteQKO5BQJjxt6QAhsDBQkFo5qABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJ
EGpaZxteQKO5oysP/1rSdvbKMzozvnVZoglnPjnSGStY9Pr2ziGL7eIMk2yt+Orr
j/AwxYIDgsZPQoJEr87eX2dCYtUMM1x+CpZsWu8dDVFLxyZp8nPmhUzcUCFfutw1
UmAVKQkOra9segZtw4HVcSctpdgLw7NHq7vIQm4knIvjWmdC15r1B6/VJJI8CeaR
Zy+ToPr9fKnYs1RNdz+DRDN2521skX1DaInhB/ALeid90rJTRujaP9XeyNb9k32K
qd3h4C0KUGIf0fNKj4mmDlNosX3V/pJZATpFiF8aVPlybHQ2W5xpn1U8FJxE4hgR
rvsZmO685Qwm6p/uRI5Eymfm8JC5OQNt9Kvs/BMhotsW0u+je8UXwnznptMILpVP
+qxNuHUe1MYLdjK21LFF+Pk5O4W1TT6mKcbisOmZuQMG5DxpzUwm1Rs5AX1omuJt
iOrmQEvmrKKWC9qbcmWW1t2scnIJsNtrsvME0UjJFz+RL6UUX3xXlLK6YOUghCr8
gZ7ZPgFqygS6tMu8TAGURzSCfijDh+eZGwqrlvngBIaO5WiNdSXC/J9aE1KThXmX
90A3Gwry+yI2kRS7o8vmghXewPTZbnG0CVHiQIH2yqFNXnhKvhaJt0g04TcnxBte
kiFqRT4K1Bb7pUIlUANmrKo9/zRCxIOopEgRH5cVQ8ZglkT0t5d3ePmAo6h0uQIN
BGPG3pABEADghhNByVoC+qCMo+SErjxz9QYA+tKoAngbgPyxxyB4RD52Z58MwVaP
+Yk0qxJYUBat3dJwiCTlUGG+yTyMOwLl7qSDr53AD5ml0hwJqnLBJ6OUyGE4ax4D
RUVBprKlDltwr98cZDgzvwEhIO2T3tNZ4vySveITj9pLonOrLkAfGXqFOqom+S37
6eZvjKTnEUbT+S0TTynwds70W31sxVUrL62qsUnmoKEnsKXk/7X8CLXWvtNqu9kf
eiXs5Jz4N6RZUqvS0WOaaWG9v1PHukTtb8RyeookhsBqf9fWOlw5foel+NQwGQjz
0D0dDTKxn2Taweq+gWNCRH7/FJNdWa9upZ2fUAjg9hN9Ow8Y5nE3J0YKCBAQTgNa
XNtsiGQjdEKYZslxZKFM34By3LD6IrkcAEPKu9plZthmqhQumqwYRAgB9O56jg3N
GDDRyAMS7y63nNphTSatpOZtPVVMtcBw5jPjMIPFfU2dlfsvmnCvru2dvfAij+Ng
EkwOLNS8rFQHMJSQysmHuAPSYT97Yl022mPrAtb9+hwtCXt3VI6dvIARl2qPyF0D
DMw2fW5E7ivhUr2WEFiBmXunrJvMIYldBzDkkBjamelPjoevR0wfoIn0x1CbSsQi
zbEs3PXHs7nGxb9TZnHY4+J94mYHdSXrImAuH/x97OnlfUpOKPv5lwARAQABiQI8
BBgBCgAmFiEEIclrHLlQcYh09k29alpnG15Ao7kFAmPG3pACGwwFCQWjmoAACgkQ
alpnG15Ao7m2/g//Y/YRM+Qhf71G0MJpAfym6ZqmwsT78qQ8T9w95ZeIRD7UUE8d
tm39kqJTGP6DuHCNYEMs2M88o0SoQsS/7j/8is7H/13F5o40DWjuQphia2BWkB1B
G4QRRIXMlrPX8PS92GDCtGfvxn90Li2FhQGZWlNFwvKUB7+/yLMsZzOwo7BS6PwC
hvI3eC7DBC8sXjJUxsrgFAkxQxSx/njP8f4HdUwhNnB1YA2/5IY5bk8QrXxzrAK1
sbIAjpJdtPYOrZByyyj4ZpRcSm3ngV2n8yd1muJ5u+oRIQoGCdEIaweCj598jNFa
k378ZA11hCyNFHjpPIKnF3tfsQ8vjDatoq4Asy+HXFuo1GA/lvNgNb3Nv4FUozuv
JYJ0KaW73FZXlFBIBkMkRQE8TspHy2v/IGyNXBwKncmkszaiiozBd+T+1NUZgtk5
9o5uKQwLHVnHIU7r/w/oN5LvLawLg2dP/f2u/KoQXMxjwLZncSH4+5tRz4oa/GMn
k4F84AxTIjGfLJeXigyP6xIPQbvJy+8iLRaCpj+v/EPwAedbRV+u0JFeqqikca70
aGN86JBOmwpU87sfFxLI7HdI02DkvlxYYK3vYlA6zEyWaeLZ3VNr6tHcQmOnFe8Q
26gcS0AQcxQZrcWTCZ8DJYF+RnXjSVRmHV/3YDts4JyMKcD6QX8s/3aaldk=
=dLmT
-----END PGP PUBLIC KEY BLOCK-----

View File

@@ -0,0 +1,44 @@
# Coder Remote
[![Visual Studio Marketplace](https://img.shields.io/visual-studio-marketplace/v/coder.coder-remote?label=Visual%20Studio%20Marketplace&color=%233fba11)](https://marketplace.visualstudio.com/items?itemName=coder.coder-remote)
[![Open VSX Version](https://img.shields.io/open-vsx/v/coder/coder-remote)](https://open-vsx.org/extension/coder/coder-remote)
[!["Join us on
Discord"](https://badgen.net/discord/online-members/coder)](https://coder.com/chat?utm_source=github.com/coder/vscode-coder&utm_medium=github&utm_campaign=readme.md)
The Coder Remote extension lets you open [Coder](https://github.com/coder/coder)
workspaces with a single click.
- Open workspaces from the dashboard in a single click.
- Automatically start workspaces when opened.
- No command-line or local dependencies required - just install your editor!
- Works in air-gapped or restricted networks. Just connect to your Coder
deployment!
- Supports multiple editors: VS Code, Cursor, and Windsurf.
> [!NOTE]
> The extension builds on VS Code-provided implementations of SSH. Make
> sure you have the correct SSH extension installed for your editor
> (`ms-vscode-remote.remote-ssh` or `codeium.windsurf-remote-openssh` for Windsurf).
![Demo](https://github.com/coder/vscode-coder/raw/main/demo.gif?raw=true)
## Getting Started
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press
enter.
```shell
ext install coder.coder-remote
```
Alternatively, manually install the VSIX from the
[latest release](https://github.com/coder/vscode-coder/releases/latest).
### Variables Reference
Coder uses `${userHome}` from VS Code's
[variables reference](https://code.visualstudio.com/docs/editor/variables-reference).
Use this when formatting paths in the Coder extension settings rather than `~`
or `$HOME`.
Example: ${userHome}/foo/bar.baz

1
.vscode/extensions/extensions.json vendored Normal file
View File

@@ -0,0 +1 @@
[{"identifier":{"id":"ms-vscode.remote-explorer","uuid":"11858313-52cc-4e57-b3e4-d7b65281e34b"},"version":"0.5.0","location":{"$mid":1,"path":"/home/lip/.vscode/extensions/ms-vscode.remote-explorer-0.5.0","scheme":"file"},"relativeLocation":"ms-vscode.remote-explorer-0.5.0","metadata":{"installedTimestamp":1765760154710,"pinned":false,"source":"gallery","id":"11858313-52cc-4e57-b3e4-d7b65281e34b","publisherId":"5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee","publisherDisplayName":"Microsoft","targetPlatform":"undefined","updated":false,"private":false,"isPreReleaseVersion":false,"hasPreReleaseVersion":false}},{"identifier":{"id":"ms-vscode-remote.remote-ssh-edit","uuid":"bfeaf631-bcff-4908-93ed-fda4ef9a0c5c"},"version":"0.87.0","location":{"$mid":1,"path":"/home/lip/.vscode/extensions/ms-vscode-remote.remote-ssh-edit-0.87.0","scheme":"file"},"relativeLocation":"ms-vscode-remote.remote-ssh-edit-0.87.0","metadata":{"installedTimestamp":1765760154710,"pinned":false,"source":"gallery","id":"bfeaf631-bcff-4908-93ed-fda4ef9a0c5c","publisherId":"ac9410a2-0d75-40ec-90de-b59bb705801d","publisherDisplayName":"Microsoft","targetPlatform":"undefined","updated":false,"private":false,"isPreReleaseVersion":false,"hasPreReleaseVersion":false}},{"identifier":{"id":"ms-vscode-remote.remote-ssh","uuid":"607fd052-be03-4363-b657-2bd62b83d28a"},"version":"0.122.0","location":{"$mid":1,"path":"/home/lip/.vscode/extensions/ms-vscode-remote.remote-ssh-0.122.0","scheme":"file"},"relativeLocation":"ms-vscode-remote.remote-ssh-0.122.0","metadata":{"installedTimestamp":1765760154710,"pinned":false,"source":"gallery","id":"607fd052-be03-4363-b657-2bd62b83d28a","publisherId":"ac9410a2-0d75-40ec-90de-b59bb705801d","publisherDisplayName":"Microsoft","targetPlatform":"undefined","updated":false,"private":false,"isPreReleaseVersion":false,"hasPreReleaseVersion":false}},{"identifier":{"id":"asvetliakov.vscode-neovim","uuid":"caf8995c-5426-4bf7-9d01-f7968ebd49bb"},"version":"1.18.24","location":{"$mid":1,"path":"/home/lip/.vscode/extensions/asvetliakov.vscode-neovim-1.18.24","scheme":"file"},"relativeLocation":"asvetliakov.vscode-neovim-1.18.24","metadata":{"installedTimestamp":1765760549617,"pinned":false,"source":"gallery","id":"caf8995c-5426-4bf7-9d01-f7968ebd49bb","publisherId":"ce6190db-6762-4c9c-99c7-1717b9504159","publisherDisplayName":"Alexey Svetliakov","targetPlatform":"undefined","updated":false,"private":false,"isPreReleaseVersion":false,"hasPreReleaseVersion":false}},{"identifier":{"id":"coder.coder-remote"},"version":"1.11.6","location":{"$mid":1,"fsPath":"/home/lip/.vscode/extensions/coder.coder-remote-1.11.6","external":"file:///home/lip/.vscode/extensions/coder.coder-remote-1.11.6","path":"/home/lip/.vscode/extensions/coder.coder-remote-1.11.6","scheme":"file"},"relativeLocation":"coder.coder-remote-1.11.6","metadata":{"isApplicationScoped":false,"isMachineScoped":false,"isBuiltin":false,"installedTimestamp":1765806965897,"pinned":false,"source":"gallery","id":"21e3a57c-bba3-4e15-a321-7b2e2dc4f39f","publisherId":"05841aa7-205b-4149-a0ec-7b56fafb33d3","publisherDisplayName":"Coder","targetPlatform":"undefined","updated":true,"private":false,"isPreReleaseVersion":false,"hasPreReleaseVersion":false,"preRelease":false}}]

View File

@@ -0,0 +1 @@
20.12.1

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="remote-ssh" Version="0.122.0" Publisher="ms-vscode-remote" />
<DisplayName>Remote - SSH</DisplayName>
<Description xml:space="preserve">Open any folder on a remote machine using SSH and take advantage of VS Code&apos;s full feature set.</Description>
<Tags>remote development,remote,ssh,remote-menu,chat-participant</Tags>
<Categories>Other</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.107.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="ms-vscode-remote.remote-ssh-edit,ms-vscode.remote-explorer" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="ui" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Code.EnabledApiProposals" Value="resolvers,tunnels,terminalDataWriteEvent,contribViewsRemote,telemetry,contribRemoteHelp" />
<Property Id="Microsoft.VisualStudio.Code.ExecutesCode" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/Microsoft/vscode-remote-release.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/Microsoft/vscode-remote-release.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/Microsoft/vscode-remote-release.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/Microsoft/vscode-remote-release/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/Microsoft/vscode-remote-release#readme" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/resources/remote-ssh.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/readme.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/resources/remote-ssh.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@@ -0,0 +1,73 @@
All VS Marketplace extensions, including these Extensions for Visual Studio Code, must also be used consistent with the VS Marketplace Terms of Use at http://aka.ms/VSMarketplace-TOU.
MICROSOFT SOFTWARE LICENSE TERMS
MICROSOFT VISUAL STUDIO CODE REMOTE DEVELOPMENT EXTENSIONS
These license terms are an agreement between you and Microsoft Corporation (or based on where you live, one of its affiliates). They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms.
IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW.
1. INSTALLATION AND USE RIGHTS.
a. General. You may use a copy of the software with each validly licensed copy of Microsoft Visual Studio Code. You may not use the software if you do not have a license for Microsoft Visual Studio Code. You may copy and install files from the software onto your application development devices, including physical devices and virtual machines or containers on those machines, which are (i) owned by you and located on-premises or hosted on your own private cloud or data center, or (ii) remote devices, virtual machines, or containers which are dedicated solely to your use and hosted for you on Microsoft Azure or by other cloud hosting providers (collectively, “Development Devices”). You and others in your organization may use these files on your Development Devices solely to develop and test applications. For clarity, “applications” means applications developed by you and others in your organization who are each licensed to use Microsoft Visual Studio Code.
b. Demo Use. The uses permitted above include use of the software in demonstrating your applications.
c. Third Party Components. The software may include third party components with separate legal notices or governed by other agreements, as may be described in the notices accompanying the software.
d. Extensions. The software gives you the option to download other Microsoft and third party software packages from our extension marketplace or package managers. Those packages are under their own licenses, and not this agreement. Microsoft does not distribute, license or provide any warranties for any of the third party packages. By accessing or using our extension marketplace, you agree to the extension marketplace terms located at https://aka.ms/vsmarketplace-ToU.
2. ONLINE SERVICES IN THE SOFTWARE. Some features of the software make use of online services to provide you with updates to the software or extensions, to download or install additional software to enable use of this software, or to enable you to retrieve content, collaborate with others, or otherwise supplement your development. As used throughout this agreement, the term “software” includes these online service features.
3. DATA.
a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the product documentation located at https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting. There may also be some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with Microsofts privacy statement. Our privacy statement is located at https://aka.ms/privacy. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://learn.microsoft.com/en-us/legal/gdpr.
4. UPDATES. The software may periodically check for updates and download and install them for you. You may obtain updates only from Microsoft or authorized sources. Microsoft may need to update your system to provide you with updates. You agree to receive these automatic updates without any additional notice. Updates may not include or support all existing software features, services, or peripheral devices. If you do not want automatic updates, you may turn them off by following the instructions in the documentation at https://go.microsoft.com/fwlink/?LinkID=616397.
5. FEEDBACK. If you give feedback about the software to Microsoft, you give to Microsoft, without charge, the right to use, share and commercialize your feedback in any way and for any purpose. You will not give feedback that is subject to a license that requires Microsoft to license its software or documentation to third parties because we include your feedback in them. These rights survive this agreement.
6. SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. For example, if Microsoft technically limits or disables extensibility for the software, you may not extend the software by, among other things, loading or injecting into the software any non-Microsoft add-ins, macros, or packages; modifying the software registry settings; or adding features or functionality equivalent to that found in other Visual Studio products. You may not:
* work around any technical limitations in the software;
* reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software, except and to the extent required by third party licensing terms governing use of certain open source components that may be included with the software;
* remove, minimize, block or modify any notices of Microsoft or its suppliers in the software;
* use the software in any way that is against the law;
* host, share, publish, rent or lease the software; or
* provide the software as a stand-alone offering or combined with any of your applications for others to use, or transfer the software or this agreement to any third party.
7. SUPPORT SERVICES. Because the software is “as is,” we may not provide support services for it.
8. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.
9. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users and end use. For further information on export restrictions, visit (aka.ms/exporting).
10. APPLICABLE LAW. If you acquired the software in the United States, Washington State law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply.
11. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you:
a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights.
b. Canada. If you acquired the software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software.
c. Germany and Austria.
(i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software.
(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable according to the statutory law.
Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence.
12. LEGAL EFFECT. This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.
13. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS-IS.” YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
14. LIMITATION ON AND EXCLUSION OF DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.
This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.
It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.
Please note: As the software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French.
Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.
EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft naccorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, dadéquation à un usage particulier et dabsence de contrefaçon sont exclues.
LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.
Cette limitation concerne :
* tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et
* les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou dune autre faute dans la limite autorisée par la loi en vigueur.
Elle sapplique également, même si Microsoft connaissait ou devrait connaître léventualité dun tel dommage. Si votre pays nautorise pas lexclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou lexclusion ci-dessus ne sappliquera pas à votre égard.
EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir dautres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.

View File

@@ -0,0 +1,2 @@
> **This is a pre-release version of this extension for early feedback and testing. This extension works best with [VS Code Insiders](https://code.visualstudio.com/insiders)**

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";exports.id=460,exports.ids=[460],exports.modules={87460:(e,t,n)=>{n.d(t,{toFormData:()=>D});var a=n(35494),r=n(98435);let i=0;const s={START_BOUNDARY:i++,HEADER_FIELD_START:i++,HEADER_FIELD:i++,HEADER_VALUE_START:i++,HEADER_VALUE:i++,HEADER_VALUE_ALMOST_DONE:i++,HEADERS_ALMOST_DONE:i++,PART_DATA_START:i++,PART_DATA:i++,END:i++};let o=1;const d=o,E=o*=2,A=e=>32|e,h=()=>{};class l{constructor(e){this.index=0,this.flags=0,this.onHeaderEnd=h,this.onHeaderField=h,this.onHeadersEnd=h,this.onHeaderValue=h,this.onPartBegin=h,this.onPartData=h,this.onPartEnd=h,this.boundaryChars={},e="\r\n--"+e;const t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n),this.boundaryChars[t[n]]=!0;this.boundary=t,this.lookbehind=new Uint8Array(this.boundary.length+8),this.state=s.START_BOUNDARY}write(e){let t=0;const n=e.length;let a=this.index,{lookbehind:r,boundary:i,boundaryChars:o,index:h,state:l,flags:D}=this;const c=this.boundary.length,f=c-1,T=e.length;let _,R;const u=e=>{this[e+"Mark"]=t},H=e=>{delete this[e+"Mark"]},b=(e,t,n,a)=>{void 0!==t&&t===n||this[e](a&&a.subarray(t,n))},L=(n,a)=>{const r=n+"Mark";r in this&&(a?(b(n,this[r],t,e),delete this[r]):(b(n,this[r],e.length,e),this[r]=0))};for(t=0;t<n;t++)switch(_=e[t],l){case s.START_BOUNDARY:if(h===i.length-2){if(45===_)D|=E;else if(13!==_)return;h++;break}if(h-1==i.length-2){if(D&E&&45===_)l=s.END,D=0;else{if(D&E||10!==_)return;h=0,b("onPartBegin"),l=s.HEADER_FIELD_START}break}_!==i[h+2]&&(h=-2),_===i[h+2]&&h++;break;case s.HEADER_FIELD_START:l=s.HEADER_FIELD,u("onHeaderField"),h=0;case s.HEADER_FIELD:if(13===_){H("onHeaderField"),l=s.HEADERS_ALMOST_DONE;break}if(h++,45===_)break;if(58===_){if(1===h)return;L("onHeaderField",!0),l=s.HEADER_VALUE_START;break}if(R=A(_),R<97||R>122)return;break;case s.HEADER_VALUE_START:if(32===_)break;u("onHeaderValue"),l=s.HEADER_VALUE;case s.HEADER_VALUE:13===_&&(L("onHeaderValue",!0),b("onHeaderEnd"),l=s.HEADER_VALUE_ALMOST_DONE);break;case s.HEADER_VALUE_ALMOST_DONE:if(10!==_)return;l=s.HEADER_FIELD_START;break;case s.HEADERS_ALMOST_DONE:if(10!==_)return;b("onHeadersEnd"),l=s.PART_DATA_START;break;case s.PART_DATA_START:l=s.PART_DATA,u("onPartData");case s.PART_DATA:if(a=h,0===h){for(t+=f;t<T&&!(e[t]in o);)t+=c;t-=f,_=e[t]}if(h<i.length)i[h]===_?(0===h&&L("onPartData",!0),h++):h=0;else if(h===i.length)h++,13===_?D|=d:45===_?D|=E:h=0;else if(h-1===i.length)if(D&d){if(h=0,10===_){D&=~d,b("onPartEnd"),b("onPartBegin"),l=s.HEADER_FIELD_START;break}}else D&E&&45===_?(b("onPartEnd"),l=s.END,D=0):h=0;if(h>0)r[h-1]=_;else if(a>0){const e=new Uint8Array(r.buffer,r.byteOffset,r.byteLength);b("onPartData",0,a,e),a=0,u("onPartData"),t--}break;case s.END:break;default:throw new Error(`Unexpected state entered: ${l}`)}L("onHeaderField"),L("onHeaderValue"),L("onPartData"),this.index=h,this.state=l,this.flags=D}end(){if(this.state===s.HEADER_FIELD_START&&0===this.index||this.state===s.PART_DATA&&this.index===this.boundary.length)this.onPartEnd();else if(this.state!==s.END)throw new Error("MultipartParser.end(): stream ended unexpectedly")}}async function D(e,t){if(!/multipart/i.test(t))throw new TypeError("Failed to fetch");const n=t.match(/boundary=(?:"([^"]+)"|([^;]+))/i);if(!n)throw new TypeError("no or bad content-type header, no multipart boundary");const i=new l(n[1]||n[2]);let s,o,d,E,A,h;const D=[],c=new r.fS,f=e=>{d+=u.decode(e,{stream:!0})},T=e=>{D.push(e)},_=()=>{const e=new a.ZH(D,h,{type:A});c.append(E,e)},R=()=>{c.append(E,d)},u=new TextDecoder("utf-8");u.decode(),i.onPartBegin=function(){i.onPartData=f,i.onPartEnd=R,s="",o="",d="",E="",A="",h=null,D.length=0},i.onHeaderField=function(e){s+=u.decode(e,{stream:!0})},i.onHeaderValue=function(e){o+=u.decode(e,{stream:!0})},i.onHeaderEnd=function(){if(o+=u.decode(),s=s.toLowerCase(),"content-disposition"===s){const e=o.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);e&&(E=e[2]||e[3]||""),h=function(e){const t=e.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);if(!t)return;const n=t[2]||t[3]||"";let a=n.slice(n.lastIndexOf("\\")+1);return a=a.replace(/%22/g,'"'),a=a.replace(/&#(\d{4});/g,((e,t)=>String.fromCharCode(t))),a}(o),h&&(i.onPartData=T,i.onPartEnd=_)}else"content-type"===s&&(A=o);o="",s=""};for await(const t of e)i.write(t);return i.end(),c}}};
//# sourceMappingURL=460.js.map

View File

@@ -0,0 +1,2 @@
(()=>{"use strict";var e={79896:e=>{e.exports=require("fs")},58611:e=>{e.exports=require("http")}},r={};function s(t){var o=r[t];if(void 0!==o)return o.exports;var n=r[t]={exports:{}};return e[t](n,n.exports,s),n.exports}var t={};(()=>{var e=t;Object.defineProperty(e,"__esModule",{value:!0});const r=s(58611),o=s(79896);function n(e){console.error(`Failed to get SSH credentials: ${e}`),console.error(JSON.stringify(process.argv)),process.exit(1)}!function(e){if(!process.env.VSCODE_SSH_ASKPASS_HANDLE)return n("Missing handle");if(!process.env.VSCODE_SSH_ASKPASS_RESULT)return n("Missing output file");const s=process.env.VSCODE_SSH_ASKPASS_RESULT,t=process.env.VSCODE_SSH_ASKPASS_HANDLE,i=e.slice(2).join(" "),S={socketPath:t,path:"/",method:"POST"},c=r.request(S,(e=>{if(200!==e.statusCode)return n(`Bad status code: ${e.statusCode}`);const r=[];e.setEncoding("utf8"),e.on("data",(e=>r.push(e))),e.on("end",(()=>{const e=r.join("");let t;try{t=JSON.parse(e)}catch(e){return n("Error parsing response:"+e.message)}t.canceled?process.exit(1):o.writeFileSync(s,t.response+"\n"),setTimeout((()=>process.exit(0)),0)}))}));c.on("error",(e=>n("Error in request: "+e.message))),c.write(JSON.stringify({request:i})),c.end()}(process.argv)})();var o=exports;for(var n in t)o[n]=t[n];t.__esModule&&Object.defineProperty(o,"__esModule",{value:!0})})();
//# sourceMappingURL=askpass-main.js.map

View File

@@ -0,0 +1,41 @@
/* Sinon.JS 9.2.4, 2021-01-23, @license BSD-3 */
/*!
diff v4.0.1
Software License Agreement (BSD License)
Copyright (c) 2009-2015, Kevin Decker <kpdecker@gmail.com>
All rights reserved.
Redistribution and use of this software in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of Kevin Decker nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@license
*/
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */

View File

@@ -0,0 +1,580 @@
## SSH tips
SSH is powerful and flexible, but this also adds some setup complexity. This section includes some tips and tricks for getting the Remote - SSH extension up and running in different environments.
### Configuring the $EDITOR variable
For macOS / linux remote hosts, add this snippet to your shell configuration file (like `.bashrc` or `.zshrc`)
```bash
if [ "$VSCODE_INJECTION" = "1" ]; then
export EDITOR="code --wait" # or 'code-insiders' if you're using VS Code Insiders
fi
```
For Windows hosts, here is the equivalent Powershell:
```pwsh
if ($env:VSCODE_INJECTION -eq "1") {
$env:EDITOR = "code --wait" # or 'code-insiders' for VS Code Insiders
}
```
Now running a terminal command that uses the $EDITOR variable, like `git commit`, will open the file in VS Code instead of the default terminal-based editor (like `vim` or `nano`).
### Configuring key based authentication
[SSH public key authentication](https://www.ssh.com/ssh/public-key-authentication) is a convenient, high security authentication method that combines a local "private" key with a "public" key that you associate with your user account on an SSH host. This section will walk you through how to generate these keys and add them to a host.
> **Tip:** PuTTY for Windows is not a [supported client](#installing-a-supported-ssh-client), but you can [convert your PuTTYGen keys](#reusing-a-key-generated-in-puttygen).
### Quick start: Using SSH keys
To set up SSH key based authentication for your remote host. First we'll create a key pair and then copy the public key to the host.
**Create your local SSH key pair**
Check to see if you already have an SSH key on your **local** machine. This is typically located at `~/.ssh/id_ed25519.pub` on macOS / Linux, and the `.ssh` directory in your user profile folder on Windows (for example `C:\Users\your-user\.ssh\id_ed25519.pub`).
If you do not have a key, run the following command in a **local** terminal / PowerShell to generate an SSH key pair:
```bash
ssh-keygen -t ed25519 -b 4096
```
> **Tip:** Don't have `ssh-keygen`? Install [a supported SSH client](#installing-a-supported-ssh-client).
**Restrict the permissions on the private key file**
- For macOS / Linux, run the following shell command, replacing the path to your private key if necessary:
```bash
chmod 400 ~/.ssh/id_ed25519
```
- For Windows, run the following command in PowerShell to grant explicit read access to your username:
```powershell
icacls "privateKeyPath" /grant <username>:R
```
Then navigate to the private key file in Windows Explorer, right-click and select **Properties**. Select the **Security** tab > **Advanced** > **Disable inheritance** > **Remove all inherited permissions from this object**.
**Authorize your macOS or Linux machine to connect**
Run one of the following commands, in a **local terminal window** replacing user and host name as appropriate to copy your local public key to the SSH host.
- Connecting to a **macOS or Linux** SSH host:
```bash
export USER_AT_HOST="your-user-name-on-host@hostname"
export PUBKEYPATH="$HOME/.ssh/id_ed25519.pub"
ssh-copy-id -i "$PUBKEYPATH" "$USER_AT_HOST"
```
- Connecting to a **Windows** SSH host:
- The host uses OpenSSH Server and the user [belongs to the administrator group](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration#authorizedkeysfile):
```bash
export USER_AT_HOST="your-user-name-on-host@hostname"
export PUBKEYPATH="$HOME/.ssh/id_ed25519.pub"
ssh $USER_AT_HOST "powershell Add-Content -Force -Path \"\$Env:PROGRAMDATA\\ssh\\administrators_authorized_keys\" -Value '$(tr -d '\n\r' < "$PUBKEYPATH")'"
```
- Otherwise:
```bash
export USER_AT_HOST="your-user-name-on-host@hostname"
export PUBKEYPATH="$HOME/.ssh/id_ed25519.pub"
ssh $USER_AT_HOST "powershell New-Item -Force -ItemType Directory -Path \"\$HOME\\.ssh\"; Add-Content -Force -Path \"\$HOME\\.ssh\\authorized_keys\" -Value '$(tr -d '\n\r' < "$PUBKEYPATH")'"
```
You may want to validate that the `authorized_keys` file in the `.ssh` folder for your **remote user on the SSH host** is owned by you and no other user has permission to access it. See the [OpenSSH wiki](https://github.com/PowerShell/Win32-OpenSSH/wiki/Security-protection-of-various-files-in-Win32-OpenSSH#authorized_keys) for details.
**Authorize your Windows machine to connect**
Run one of the following commands, in a **local PowerShell** window replacing user and host name as appropriate to copy your local public key to the SSH host.
- Connecting to a **macOS or Linux** SSH host:
```powershell
$USER_AT_HOST="your-user-name-on-host@hostname"
$PUBKEYPATH="$HOME\.ssh\id_ed25519.pub"
$pubKey=(Get-Content "$PUBKEYPATH" | Out-String); ssh "$USER_AT_HOST" "mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo '${pubKey}' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
```
- Connecting to a **Windows** SSH host:
- The host uses OpenSSH Server and the user [belongs to the administrator group](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration#authorizedkeysfile):
```powershell
$USER_AT_HOST="your-user-name-on-host@hostname"
$PUBKEYPATH="$HOME\.ssh\id_ed25519.pub"
Get-Content "$PUBKEYPATH" | Out-String | ssh $USER_AT_HOST "powershell `"Add-Content -Force -Path `"`$Env:PROGRAMDATA\ssh\administrators_authorized_keys`" `""
```
- Otherwise:
```powershell
$USER_AT_HOST="your-user-name-on-host@hostname"
$PUBKEYPATH="$HOME\.ssh\id_ed25519.pub"
Get-Content "$PUBKEYPATH" | Out-String | ssh $USER_AT_HOST "powershell `"New-Item -Force -ItemType Directory -Path `"`$HOME\.ssh`"; Add-Content -Force -Path `"`$HOME\.ssh\authorized_keys`" `""
```
Validate that the `authorized_keys` file in the `.ssh` folder for your **remote user on the SSH host** is owned by you and no other user has permission to access it. See the [OpenSSH wiki](https://github.com/PowerShell/Win32-OpenSSH/wiki/Security-protection-of-various-files-in-Win32-OpenSSH#authorized_keys) for details.
### Improving your security with a dedicated key
While using a single SSH key across all your SSH hosts can be convenient, if anyone gains access to your private key, they will have access to all of your hosts as well. You can prevent this by creating a separate SSH key for your development hosts. Just follow these steps:
1. Generate a separate SSH key in a different file.
**macOS / Linux**: Run the following command in a **local terminal**:
```bash
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519-remote-ssh
```
**Windows**: Run the following command in a **local PowerShell**:
```powershell
ssh-keygen -t ed25519 -f "$HOME\.ssh\id_ed25519-remote-ssh"
```
2. Follow the same steps in the [quick start](#quick-start-using-ssh-keys) to authorize the key on the SSH host, but set the `PUBKEYPATH` to the `id_ed25519-remote-ssh.pub` file instead.
3. In VS Code, run **Remote-SSH: Open Configuration File...** in the Command Palette (`kbstyle(F1)`), select an SSH config file, and add (or modify) a host entry as follows:
```yaml
Host name-of-ssh-host-here
User your-user-name-on-host
HostName host-fqdn-or-ip-goes-here
IdentityFile ~/.ssh/id_ed25519-remote-ssh
```
> **Tip:** You can use `/` for Windows paths as well. If you use `\` you will need to use two slashes. For example, `C:\\path\\to\\my\\id_ed25519`.
### Reusing a key generated in PuTTYGen
If you used PuTTYGen to set up SSH public key authentication for the host you are connecting to, you need to convert your private key so that other SSH clients can use it. To do this:
1. Open PuTTYGen **locally** and load the private key you want to convert.
2. Select **Conversions > Export OpenSSH key** from the application menu. Save the converted key to a **local** location under the`.ssh` directory in your user profile folder (for example `C:\Users\youruser\.ssh`).
3. Validate that this new **local** file is owned by you and no other user has permissions to access it.
4. In VS Code, run **Remote-SSH: Open Configuration File...** in the Command Palette (`kbstyle(F1)`), select the SSH config file you want to change, and add (or modify) a host entry in the config file as follows to point to the file:
```yaml
Host name-of-ssh-host-here
User your-user-name-on-host
HostName host-fqdn-or-ip-goes-here
IdentityFile ~/.ssh/exported-keyfile-from-putty
```
### Improving security on multi-user servers
The Remote - SSH extension installs and maintains the "VS Code Server". The server is started with a randomly generated key, and any new connection to the server needs to provide the key. The key is stored on the remote's disk, readable only by the current user. There is one HTTP path that is available without authentication at `/version`.
By default, the server listens to `localhost` on a random TCP port that is then forwarded to your local machine. If you are connecting to a **Linux or macOS** host, you can switch to using Unix sockets that are locked down to a particular user. This socket is then forwarded instead of the port.
> **Note:** This setting **disables connection multiplexing** so configuring [public key authentication](#configuring-key-based-authentication) is recommended.
To configure it:
1. Ensure you have a **local OpenSSH 6.7+ SSH client** on Windows, macOS, or Linux and an **OpenSSH 6.7+ Linux or macOS Host** (Windows does not support this mode).
2. Switch Remote - SSH into socket mode by enabling **Remote.SSH: Remote Server Listen On Socket** in your **local** VS Code [User settings](/docs/getstarted/settings.md).
![Listen on socket VS Code setting](images/ssh/ssh-listen-on-socket.png)
3. If you've already connected to the SSH Host, select **Remote-SSH: Kill VS Code Server on Host...** from the Command Palette (`kbstyle(F1)`) so the setting takes effect.
If you encounter an error when connecting, you may need to enable socket forwarding on your SSH Host's [sshd config](https://www.ssh.com/ssh/sshd_config/). To do so:
1. Open `/etc/ssh/sshd_config` in a text editor (like vi, nano, or pico) on the **SSH host** (not locally).
2. Add the setting `AllowStreamLocalForwarding yes`.
3. Restart the SSH server. (On Ubuntu, run `sudo systemctl restart sshd`.).
4. Retry.
### Troubleshooting hanging or failing connections
If you are running into problems with VS Code hanging while trying to connect (and potentially timing out), there are a few things you can do to try to resolve the issue.
**General troubleshooting: Remove the server**
One command helpful to troubleshoot a variety of Remote-SSH issues is **Remote-SSH: Kill VS Code Server on Host**. This will remove the server, which can fix a wide range of issues and error messages you may see, such as "Could not establish connection to `server_name`: The VS Code Server failed to start."
**See if VS Code is waiting on a prompt**
Enable the `remote.SSH.showLoginTerminal` [setting](/docs/getstarted/settings.md) in VS Code and retry. If you are prompted to input a password or token, see [Enabling alternate SSH authentication methods](#enabling-alternate-ssh-authentication-methods) for details on reducing the frequency of prompts.
If you are still having trouble, set the following properties in `settings.json` and retry:
```json
"remote.SSH.showLoginTerminal": true,
"remote.SSH.useLocalServer": false
```
**Work around a bug with some versions of Windows OpenSSH server**
Due to a bug in certain versions of OpenSSH server for Windows, the default check to determine if the host is running Windows may not work properly. This does not occur with OpenSSH server that ships with Windows 1909 and below.
Fortunately, you can work around this problem by specifically telling VS Code if your SSH host is running Windows by adding the following to `settings.json`:
```json
"remote.SSH.useLocalServer": false
```
You can also force VS Code to identify a particular host as Windows using the following property:
```json
"remote.SSH.remotePlatform": {
"host-in-ssh-config-or-fqdn": "windows"
}
```
A fix has been merged so this problem should be resolved in a version of the server greater than 8.1.0.0.
**Enable TCP Forwarding on the remote host**
Remote - SSH extension makes use of an SSH tunnel to facilitate communication with the host. In some cases, this may be disabled on your SSH server. To see if this is the problem, open the **Remote - SSH** category in the output window and check for the following message:
```
open failed: administratively prohibited: open failed
```
If you do see that message, follow these steps to update your SSH server's [sshd config](https://www.ssh.com/ssh/sshd_config/):
1. Open `/etc/ssh/sshd_config` or `C:\ProgramData\ssh\sshd_config` in a text editor (like Vim, nano, Pico, or Notepad) on the **SSH host** (not locally).
2. Add the setting `AllowTcpForwarding yes`.
3. Restart the SSH server. (On Ubuntu, run `sudo systemctl restart sshd`. On Windows, in an admin PowerShell run, `Restart-Service sshd`).
4. Retry.
**Set the ProxyCommand parameter in your SSH config file**
If you are behind a proxy and are unable to connect to your SSH host, you may need to use the `ProxyCommand` parameter for your host in a **local** [SSH config file](https://linux.die.net/man/5/ssh_config). You can read this [SSH ProxyCommand article](https://www.cyberciti.biz/faq/linux-unix-ssh-proxycommand-passing-through-one-host-gateway-server/) for an example of its use.
**Ensure the remote machine has internet access**
The remote machine must have internet access to be able to download the VS Code Server and extensions from the Marketplace. See the [FAQ for details](/docs/remote/faq.md#what-are-the-connectivity-requirements-for-vs-code-server) on connectivity requirements.
**Set HTTP_PROXY / HTTPS_PROXY on the remote host**
If your remote host is behind a proxy, you may need to set the HTTP_PROXY or HTTPS_PROXY environment variable on the **SSH host**. Open your `~/.bashrc` file add the following (replacing `proxy.fqdn.or.ip:3128` with the appropriate hostname / IP and port):
```bash
export HTTP_PROXY=http://proxy.fqdn.or.ip:3128
export HTTPS_PROXY=$HTTP_PROXY
# Or if an authenticated proxy
export HTTP_PROXY=http://username:password@proxy.fqdn.or.ip:3128
export HTTPS_PROXY=$HTTP_PROXY
```
**Work around `/tmp` mounted with `noexec`**
Some remote servers are set up to disallow executing scripts from `/tmp`. VS Code writes its install script to the system temp directory and tries to execute it from there. You can work with your system administrator to determine whether this can be worked around.
**Check whether a different shell is launched during install**
Some users launch a different shell from their `.bash_profile` or other startup script on their **SSH host** because they want to use a different shell than the default. This can break VS Code's remote server install script and isn't recommended. Instead, use `chsh` to change your default shell on the remote machine.
**Connecting to systems that dynamically assign machines per connection**
Some systems will dynamically route an SSH connection to one node from a cluster each time an SSH connection is made. This is an issue for VS Code because it makes two connections to open a remote window: the first to install or start the VS Code Server (or find an already running instance) and the second to create the SSH port tunnel that VS Code uses to talk to the server. If VS Code is routed to a different machine when it creates the second connection, it won't be able to talk to the VS Code server.
One workaround for this is to use the `ControlMaster` option in OpenSSH (macOS/Linux clients only), described in [Enabling alternate SSH authentication methods](#enabling-alternate-ssh-authentication-methods), so that VS Code's two connections will be multiplexed through a single SSH connection to the same node.
**Contact your system administrator for configuration help**
SSH is a very flexible protocol and supports many configurations. If you see other errors, in either the login terminal or the **Remote-SSH** output window, they could be due to a missing setting.
Contact your system administrator for information about the required settings for your SSH host and client. Specific command-line arguments for connecting to your SSH host can be added to an [SSH config file](https://linux.die.net/man/5/ssh_config).
To access your config file, run **Remote-SSH: Open Configuration File...** in the Command Palette (`kbstyle(F1)`). You can then work with your admin to add the necessary settings.
### Enabling alternate SSH authentication methods
If you are connecting to an SSH remote host and are either:
- Connecting with two-factor authentication
- Using password authentication
- Using an SSH key with a passphrase when the [SSH Agent](#setting-up-the-ssh-agent) is not running or accessible
then VS Code should automatically prompt you to enter needed information. If you do not see the prompt, enable the `remote.SSH.showLoginTerminal` [setting](/docs/getstarted/settings.md) in VS Code. This setting displays the terminal whenever VS Code runs an SSH command. You can then enter your authentication code, password, or passphrase when the terminal appears.
If you are still having trouble, you may need to add the following properties in `settings.json` and retry:
```json
"remote.SSH.showLoginTerminal": true,
"remote.SSH.useLocalServer": false
```
If you are on macOS and Linux and want to reduce how often you have to enter a password or token, you can enable the `ControlMaster` feature on your **local machine** so that OpenSSH runs multiple SSH sessions over a single connection.
To enable `ControlMaster`:
1. Add an entry like this to your SSH config file:
```yaml
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
```
2. Then run `mkdir -p ~/.ssh/sockets` to create the sockets folder.
### Setting up the SSH Agent
If you are connecting to an SSH host using a key with a passphrase, you should ensure that the [SSH Agent](https://www.ssh.com/ssh/agent) is running **locally**. VS Code will automatically add your key to the agent so you don't have to enter your passphrase every time you open a remote VS Code window.
To verify that the agent is running and is reachable from VS Code's environment, run `ssh-add -l` in the terminal of a local VS Code window. You should see a listing of the keys in the agent (or a message that it has no keys). If the agent is not running, follow these instructions to start it. After starting the agent, be sure to restart VS Code.
**Windows:**
To enable SSH Agent automatically on Windows, start a **local Administrator PowerShell** and run the following commands:
```powershell
# Make sure you're running as an Administrator
Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
Get-Service ssh-agent
```
Now the agent will be started automatically on login.
**Linux:**
To start the SSH Agent in the background, run:
```bash
eval "$(ssh-agent -s)"
```
To start the SSH Agent automatically on login, add these lines to your `~/.bash_profile`:
```bash
if [ -z "$SSH_AUTH_SOCK" ]; then
# Check for a currently running instance of the agent
RUNNING_AGENT="`ps -ax | grep 'ssh-agent -s' | grep -v grep | wc -l | tr -d '[:space:]'`"
if [ "$RUNNING_AGENT" = "0" ]; then
# Launch a new instance of the agent
ssh-agent -s &> .ssh/ssh-agent
fi
eval `cat .ssh/ssh-agent`
fi
```
**macOS:**
The agent should be running by default on macOS.
### Making local SSH Agent available on the remote
An SSH Agent on your local machine allows the Remote - SSH extension to connect to your chosen remote system without repeatedly prompting for a passphrase, but tools like Git that run on the remote, don't have access to your locally-unlocked private keys.
You can see this by opening the integrated terminal on the remote and running `ssh-add -l`. The command should list the unlocked keys, but instead reports an error about not being able to connect to the authentication agent. Setting `ForwardAgent yes` makes the local SSH Agent available in the remote environment, solving this problem.
You can do this by editing your `.ssh/config` file (or whatever `Remote.SSH.configFile` is set to - use the **Remote-SSH: Open SSH Configuration File...** command to be sure) and adding:
```ssh-config
Host *
ForwardAgent yes
```
Note that you might want to be more restrictive and only set the option for particular named hosts.
### Fixing SSH file permission errors
SSH can be strict about file permissions and if they are set incorrectly, you may see errors such as "WARNING: UNPROTECTED PRIVATE KEY FILE!". There are several ways to update file permissions in order to fix this, which are described in the sections below.
### Local SSH file and folder permissions
**macOS / Linux:**
On your local machine, make sure the following permissions are set:
| Folder / File | Permissions |
| ----------------------------------------- | --------------------------------- |
| `.ssh` in your user folder | `chmod 700 ~/.ssh` |
| `.ssh/config` in your user folder | `chmod 600 ~/.ssh/config` |
| `.ssh/id_ed25519.pub` in your user folder | `chmod 600 ~/.ssh/id_ed25519.pub` |
| Any other key file | `chmod 600 /path/to/key/file` |
**Windows:**
The specific expected permissions can vary depending on the exact SSH implementation you are using. We recommend using the out of box [Windows 10 OpenSSH Client](https://learn.microsoft.com/windows-server/administration/openssh/openssh_overview).
In this case, make sure that all of the files in the `.ssh` folder for your remote user on the SSH host is owned by you and no other user has permissions to access it. See the [Windows OpenSSH wiki](https://github.com/PowerShell/Win32-OpenSSH/wiki/Security-protection-of-various-files-in-Win32-OpenSSH) for details.
For all other clients, consult your client's documentation for what the implementation expects.
### Server SSH file and folder permissions
**macOS / Linux:**
On the remote machine you are connecting to, make sure the following permissions are set:
| Folder / File | Linux / macOS Permissions |
| -------------------------------------------------------- | ---------------------------------- |
| `.ssh` in your user folder on the server | `chmod 700 ~/.ssh` |
| `.ssh/authorized_keys` in your user folder on the server | `chmod 600 ~/.ssh/authorized_keys` |
Note that only Linux hosts are currently supported, which is why permissions for macOS and Windows 10 have been omitted.
**Windows:**
See the [Windows OpenSSH wiki](https://github.com/PowerShell/Win32-OpenSSH/wiki/Security-protection-of-various-files-in-Win32-OpenSSH) for details on setting the appropriate file permissions for the Windows OpenSSH server.
### Installing a supported SSH client
| OS | Instructions |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Windows 10 1803+ / Server 2016/2019 1803+ | Install the [Windows OpenSSH Client](https://learn.microsoft.com/windows-server/administration/openssh/openssh_install_firstuse). |
| Earlier Windows | Install [Git for Windows](https://git-scm.com/download/win). |
| macOS | Comes pre-installed. |
| Debian/Ubuntu | Run `sudo apt-get install openssh-client` |
| RHEL / Fedora / CentOS | Run `sudo yum install openssh-clients` |
VS Code will look for the `ssh` command in the PATH. Failing that, on Windows it will attempt to find `ssh.exe` in the default Git for Windows install path. You can also specifically tell VS Code where to find the SSH client by adding the `remote.SSH.path` property to `settings.json`.
### Installing a supported SSH server
| OS | Instructions | Details |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Debian 8+ / Ubuntu 16.04+ | Run `sudo apt-get install openssh-server` | See the [Ubuntu SSH](https://help.ubuntu.com/community/SSH?action=show) documentation for details. |
| RHEL / CentOS 7+ | Run `sudo yum install openssh-server && sudo systemctl start sshd.service && sudo systemctl enable sshd.service` | See the [RedHat SSH](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/ch-openssh) documentation for details. |
| SuSE 12+ / openSUSE 42.3+ | In Yast, go to Services Manager, select "sshd" in the list, and click **Enable**. Next go to Firewall, select the **Permanent** configuration, and under services check **sshd**. | See the [SuSE SSH](https://en.opensuse.org/OpenSSH) documentation for details. |
| Windows 10 1803+ / Server 2016/2019 1803+ | Install the [Windows OpenSSH Server](https://learn.microsoft.com/windows-server/administration/openssh/openssh_install_firstuse). |
| macOS 10.14+ (Mojave) | Enable [Remote Login](https://support.apple.com/guide/mac-help/allow-a-remote-computer-to-access-your-mac-mchlp1066/mac). | |
### Resolving hangs when doing a Git push or sync on an SSH host
If you clone a Git repository using SSH and your SSH key has a passphrase, VS Code's pull and sync features may hang when running remotely.
Either use an SSH key without a passphrase, clone using HTTPS, or run `git push` from the command line to work around the issue.
### Using SSHFS to access files on your remote host
[SSHFS](https://en.wikipedia.org/wiki/SSHFS) is a secure remote filesystem access protocol that builds up from SFTP. It provides advantages over something like a CIFS / Samba share in that all that is required is SSH access to the machine.
> **Note:** For performance reasons, SSHFS is best used for single file edits and uploading/downloading content. If you need to use an application that bulk reads/write to many files at once (like a local source control tool), [rsync](#using-rsync-to-maintain-a-local-copy-of-your-source-code) is a better choice.
**macOS / Linux**:
On Linux, you can use your distribution's package manager to install SSHFS. For Debian/Ubuntu: `sudo apt-get install sshfs`
> **Note:** WSL 1 does not support FUSE or SSHFS, so the instructions differ for Windows currently. **WSL 2 does include FUSE and SSHFS support**, so this will change soon.
On macOS, you can install SSHFS using [Homebrew](https://brew.sh/):
```bash
brew install --cask macfuse
brew install gromgit/fuse/sshfs-mac
brew link --overwrite sshfs-mac
```
In addition, if you would prefer not to use the command line to mount the remote filesystem, you can also install [SSHFS GUI](https://github.com/dstuecken/sshfs-gui).
To use the command line, run the following commands from a local terminal (replacing `user@hostname` with the remote user and hostname / IP):
```bash
export USER_AT_HOST=user@hostname
# Make the directory where the remote filesystem will be mounted
mkdir -p "$HOME/sshfs/$USER_AT_HOST"
# Mount the remote filesystem
sshfs "$USER_AT_HOST:" "$HOME/sshfs/$USER_AT_HOST" -ovolname="$USER_AT_HOST" -p 22 \
-o workaround=nonodelay -o transform_symlinks -o idmap=user -C
```
This will make your home folder on the remote machine available under the `~/sshfs`. When you are done, you can unmount it using your OS's Finder / file explorer or by using the command line:
```bash
umount "$HOME/sshfs/$USER_AT_HOST"
```
**Windows:**
Follow these steps:
1. On Linux, add `.gitattributes` file to your project to **force consistent line endings** between Linux and Windows to avoid unexpected issues due to CRLF/LF differences between the two operating systems. See [Resolving Git line ending issues](#resolving-git-line-ending-issues-in-wsl-resulting-in-many-modified-files) for details.
2. Next, install [SSHFS-Win](https://github.com/billziss-gh/sshfs-win) using [Chocolatey](https://chocolatey.org/): `choco install sshfs`
3. Once you've installed SSHFS for Windows, you can use the File Explorer's **Map Network Drive...** option with the path `\\sshfs\user@hostname`, where `user@hostname` is your remote user and hostname / IP. You can script this using the command prompt as follows: `net use /PERSISTENT:NO X: \\sshfs\user@hostname`
4. Once done, disconnect by right-clicking on the drive in the File Explorer and selecting **Disconnect**.
### Connect to a remote host from the terminal
Once a host has been configured, you can connect to it directly from the terminal by passing a remote URI.
For example, to connect to `remote_server` and open the `/code/my_project` folder, run:
```bash
code --remote ssh-remote+remote_server /code/my_project
```
We need to do some guessing on whether the input path is a file or a folder. If it has a file extension, it is considered a file.
To force that a folder is opened, add slash to the path or use:
`code --folder-uri vscode-remote://ssh-remote+remote_server/code/folder.with.dot`
To force that a file is opened, add `--goto` or use:
`code --file-uri vscode-remote://ssh-remote+remote_server/code/fileWithoutExtension`
### Using rsync to maintain a local copy of your source code
An alternative to [using SSHFS to access remote files](#using-sshfs-to-access-files-on-your-remote-host) is to [use `rsync`](https://rsync.samba.org/) to copy the entire contents of a folder on remote host to your local machine. The `rsync` command will determine which files need to be updated each time it is run, which is far more efficient and convenient than using something like `scp` or `sftp`. This is primarily something to consider if you really need to use multi-file or performance intensive local tools.
The `rsync` command is available out of box on macOS and can be installed using Linux package managers (for example `sudo apt-get install rsync` on Debian/Ubuntu). For Windows, you'll need to either use [WSL](https://learn.microsoft.com/windows/wsl/install) or [Cygwin](https://www.cygwin.com/) to access the command.
To use the command, navigate to the folder you want to store the synched contents and run the following replacing `user@hostname` with the remote user and hostname / IP and `/remote/source/code/path` with the remote source code location.
On **macOS, Linux, or inside WSL**:
```bash
rsync -rlptzv --progress --delete --exclude=.git "user@hostname:/remote/source/code/path" .
```
Or using **WSL from PowerShell on Windows**:
```powershell
wsl rsync -rlptzv --progress --delete --exclude=.git "user@hostname:/remote/source/code/path" "`$(wslpath -a '$PWD')"
```
You can rerun this command each time you want to get the latest copy of your files and only updates will be transferred. The `.git` folder is intentionally excluded both for performance reasons and so you can use local Git tools without worrying about the state on the remote host.
To push content, reverse the source and target parameters in the command. However, **on Windows** you should add a `.gitattributes` file to your project to **force consistent line endings** before doing so. See [Resolving Git line ending issues](#resolving-git-line-ending-issues-in-wsl-resulting-in-many-modified-files) for details.
```bash
rsync -rlptzv --progress --delete --exclude=.git . "user@hostname:/remote/source/code/path"
```
### Cleaning up the VS Code Server on the remote
The SSH extension provides a command for cleaning up the VS Code Server from the remote machine, **Remote-SSH: Uninstall VS Code Server from Host...**. The command does two things: it kills any running VS Code Server processes and it deletes the folder where the server was installed.
If you want to run these steps manually, or if the command isn't working for you, you can run a script like this:
```bash
# Kill server processes
kill -9 $(ps aux | grep vscode-server | grep $USER | grep -v grep | awk '{print $2}')
# Delete related files and folder
rm -rf $HOME/.vscode-server # Or ~/.vscode-server-insiders
```
The VS Code Server was previously installed under `~/.vscode-remote` so you can check that location too.
### SSH into a remote WSL 2 host
You may want to use SSH to connect to a WSL distro running on your remote machine. Check out [this guide](https://www.hanselman.com/blog/the-easy-way-how-to-ssh-into-bash-and-wsl2-on-windows-10-from-an-external-machine) to learn how to SSH into Bash and WSL 2 on Windows 10 from an external machine.

View File

@@ -0,0 +1,62 @@
This document is about troubleshooting and filing issue reports for Remote-SSH.
**To learn more about Remote - SSH, see the [documentation on `code.visualstudio.com`](https://code.visualstudio.com/docs/remote/remote-overview).**
**Please also see [SSH Tips & Tricks](https://code.visualstudio.com/docs/remote/troubleshooting#_ssh-tips) for many common workarounds and configuration tips.**
Going through these steps before submitting an issue is **required** as it may get address your issue or help us to narrow it down.
## Troubleshooting Steps
1. **Find the log** at View > Output > Remote-SSH. Skim it for obvious errors. For example, if ssh reported an error while connecting, and the extension didn't surface it to the VS Code UI, you may notice it in here. **Always include this full log when filing an issue.**
<img width="753" alt="image" src="https://github.com/microsoft/vscode-remote-release/assets/323878/890155fc-89a4-4014-be30-680f3256edf6">
2. Search the log for `Running ssh connection command` and copy the command directly after. This is the exact ssh command that the extension attempted to use. Try running this command in an external terminal to check that you are able to connect to your host. If you are unable to connect to your remote machine from the command line this means you likely have an issue with your SSH configuration.
3. Now try running this command but add `echo "echo hello" |` to the beginning to confirm that we can execute a script on your remote by piping it into SSH. This is essentially how we run a script over ssh to install the VS Code Server on your remote, and some advanced configurations break this.
4. Try connecting using both values of the `remote.SSH.useLocalServer` setting. This setting is described in more detail below.
5. If `remote.SSH.useExecServer` is enabled on your machine, try disabling it and see whether that is successful. This setting is described in more detail below.
### `remote.SSH.useLocalServer` - Connection Mode
The Remote-SSH extension has a setting called `remote.SSH.useLocalServer` which provides two different modes for connecting. The default value is `true` which is called "Local Server Mode" and when `false` it is "Terminal Mode". This setting by default is disabled on Windows, and to enable it on Windows you **must** enable it directly in your settings.json, not through the settings UI. The two options are described below:
- `true`: "Local Server Mode": The Remote-SSH extension spawns an SSH process which will then be reused by all VS Code windows connected to that remote.
- `false`: "Terminal Mode": In this mode, the Remote-SSH extension runs the SSH connection command in a hidden terminal. This means that each VS Code window has its own connection. For example, if you enter a password when connecting, you will have to enter it for each window, or for each time a window reloads.
### `remote.SSH.useExecServer` - Exec Server Mode
The Remote-SSH extension can establish itself by bootstrapping with a minimal control server before launching the full VS Code Server. This enables other functionality such as connecting to Dev Containers or WSL over SSH. This is gradually being enabled by default for users.
## Other Suggestions
1. Run the command "Kill VS Code Server on Host..." from the command palette. This will kill running VS Code Server processes on your remote machine and remove server files from the remote.
2. If the log implies that the SSH connection was established successfully, use Help > Toggle Developer Tools > Console and check for other errors that may have come from the VS Code window.
3. Some issues may be caused by the VS Code Server failing to start on your remote, you may find more details in the server log. The location differs depending on the value of `remote.SSH.useExecServer`. It will be printed in the log when starting the server.
- `false`: `~/.vscode-server[-insiders]/.<hash>.log`
- `true`: `~/.vscode-server[-insiders]/cli/servers/*/log.txt`
4. If you think a recent regression was introduced to VS Code core, using the [vscode-bisect](https://github.com/microsoft/vscode-bisect) tool to narrow down the specific version is very helpful in a bug report.
## Performance Issues
If you are seeing issues involving processes using high CPU/RAM on your _local_ machine, see the [Performance Issues wiki page](https://github.com/microsoft/vscode/wiki/Performance-Issues) on the vscode repo.
If you are seeing this with processes on the remote machine that Remote-SSH has connected to, there are a few things you can do to narrow down the problem. See the [VS Code Remote Development Overview](https://code.visualstudio.com/docs/remote/remote-overview) doc page for a description of what these processes are.
First, you should determine exactly which process is having the issue. Use the built-in Process Explorer (Help > Open Process Explorer) or a separate command like `ps -eo pid,%cpu,%mem,command` to find the full command arguments of the process having the issue. If the process is the extension-host process, or a child process belonging to an extension, then it's likely that an extension you installed is misbehaving. Try reloading the window with extensions disabled, or run the command "Start Extension Bisect" to determine which extension is causing the issue. This is the most likely issue.
If the issue is with the VS Code Server process (`server-main.js`) you can follow the steps described in [Profiling Remote Processes](https://github.com/microsoft/vscode-remote-release/wiki/Profiling-Remote-Processes) to get a profile to include in an issue.
## Native Extension Host Crashes
If it appears through logs that the remote extension host process is exiting unexpected, it may be helpful to capture a coredump and include that in an issue. An example error with signal `SIGSEGV` is shown below.
```
2024-01-01 12:00:00.000 [info] [<unknown>][111a1aa1][ExtensionHostConnection] <12345> Extension Host Process exited with code: null, signal: SIGSEGV.
```
Please see https://github.com/microsoft/vscode/wiki/Native-Crash-Issues#remote-extension-host-crashes for more information.
## Filing a bug
Still have questions after reading this wiki, or think you're hitting a bug in the extension. Please use [this issue template](https://github.com/microsoft/vscode-remote-release/issues/new?template=a_remote_ssh_bug_form.yml) to open an issue on this repo.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'

View File

@@ -0,0 +1,452 @@
# shellcheck shell=sh
echo "#<<startMarker>>"
echo "Script executing under PID: $$"
VSC_TMP="${XDG_RUNTIME_DIR:-/tmp}"
# Configuration
UUID="#<<uuid>>"
COMMIT_ID="#<<commitId>>"
QUALITY="#<<quality>>"
TOKEN="#<<token>>"
VSCODE_AGENT_FOLDER="#<<vscodeAgentFolder>>"
ALLOW_CLIENT_DOWNLOAD="#<<allowClientDownload>>"
FORCE_CLIENT_DOWNLOAD="#<<forceClientDownload>>"
CLI_NAME_IN_ARCHIVE="#<<cliNameInArchive>>"
IGNORE_WGET_CONFIG_FLAG="#<<ignoreWgetConfigFlag>>"
IGNORE_CURL_CONFIG_FLAG="#<<ignoreCurlConfigFlag>>"
WGET_TRIES_SEGMENT="#<<wgetTriesSegment>>"
LISTEN_ARGS="#<<listenArgs>>"
VERBOSE="#<<verbose>>"
HTTP_PROXY_INPUT="#<<httpProxy>>"
HTTPS_PROXY_INPUT="#<<httpsProxy>>"
RECONNECTION_GRACE_TIME="#<<reconnectionGraceTime>>"
CLI_NAME_ON_DISK="${CLI_NAME_IN_ARCHIVE}-${COMMIT_ID}"
CLI_PATH="${VSCODE_AGENT_FOLDER}/${CLI_NAME_ON_DISK}"
CLI_LOG_FILE="${VSCODE_AGENT_FOLDER}/.cli.${COMMIT_ID}.log"
# Marker strings
GET_DOWNLOAD_SERVER_START_TRIGGER="#<<getDownloadServerStartTrigger>>"
GET_DOWNLOAD_SERVER_END_TRIGGER="#<<getDownloadServerEndTrigger>>"
GET_PROGRESS_DOWNLOADING="#<<getProgressDownloading>>"
GET_PROGRESS_INSTALLING="#<<getProgressInstalling>>"
UNPACK_RESULT=
DID_LOCAL_DOWNLOAD=0
DOWNLOAD_TIME=
INSTALL_TIME=
SERVER_START_TIME=
fail_with_exitcode() {
echo "${UUID}: start"
echo "exitCode==$1=="
echo_common_results
echo "${UUID}: end"
exit 0
}
echo_common_results() {
echo "listeningOn==$LISTENING_ON=="
echo "osReleaseId==$OSRELEASEID=="
echo "arch==$ARCH=="
echo "vscodeArch==$VSCODE_ARCH=="
echo "bitness==$BITNESS=="
echo "tmpDir==$VSC_TMP=="
echo "platform==$PLATFORM=="
echo "unpackResult==$UNPACK_RESULT=="
echo "didLocalDownload==$DID_LOCAL_DOWNLOAD=="
echo "downloadTime==$DOWNLOAD_TIME=="
echo "installTime==$INSTALL_TIME=="
echo "serverStartTime==$SERVER_START_TIME=="
echo "execServerToken==${TOKEN}=="
echo "platformDownloadPath==$PLATFORM_DOWNLOAD_PATH=="
#<<printVars>>
}
millis() {
d=$(date +%s%N)
# Test if 'd' contains any non-digit characters
if [ -z "${d##*[!0-9]*}" ] || [ "$PLATFORM" != "linux" ]; then
# Fallback for MacOS or any date binary that doesn't handle '%N'
echo $(($(date +%s)*1000))
return
fi
echo $((d/1000000))
}
elapsed() {
echo $(($(millis) - $1))
}
printenv_indent() {
if [ "$VERBOSE" = "1" ]; then
echo "printenv:"
printenv | sed 's/^/ /'
fi
}
[ -z "${http_proxy}" ] && [ "${HTTP_PROXY_INPUT}" != "" ] && export http_proxy="$HTTP_PROXY_INPUT"
[ -z "${https_proxy}" ] && [ "${HTTPS_PROXY_INPUT}" != "" ] && export https_proxy="$HTTPS_PROXY_INPUT"
#
# Get OS name
#
OSRELEASEID=$(cat /etc/os-release 2>/dev/null | grep -a -E '^ID=' | sed 's/^[Ii][Dd]=//g' | sed 's/"//g')
if [ -z "$OSRELEASEID" ]
then
OSRELEASEID=$(cat /usr/lib/os-release 2>/dev/null | grep -a -E '^ID=' | sed 's/^[Ii][Dd]=//g' | sed 's/"//g')
if [ -z "$OSRELEASEID" ]
then
OSRELEASEID=$(uname -s)
fi
fi
#
# Get host platform/architecture
#
UNAME=$(uname -s)
case $UNAME in
Linux) PLATFORM=linux;;
Darwin) PLATFORM=macOS;;
*)
echo "Unsupported platform: $UNAME"
fail_with_exitcode #<<ExitCode.UnsupportedPlatform>>
;;
esac
BITNESS=$(getconf LONG_BIT)
ARCH=$(uname -m)
case $ARCH in
x86_64) VSCODE_ARCH="x64";;
armv7l | armv8l)
VSCODE_ARCH="armhf"
;;
arm64 | aarch64)
if [ "$BITNESS" = 32 ]; then
# Can have 32-bit userland on 64-bit kernel
VSCODE_ARCH="armhf"
else
VSCODE_ARCH="arm64"
fi
;;
*)
OSRELEASE=$(uname -r)
case $OSRELEASE in
*x86_64*) VSCODE_ARCH="x64";;
*)
echo "Unsupported architecture: $ARCH"
fail_with_exitcode #<<ExitCode.UnsupportedArch>>
;;
esac
;;
esac
if [ "$PLATFORM" = linux ]; then
if [ "$VSCODE_ARCH" = armhf ]; then
PLATFORM_DOWNLOAD_PATH=cli-linux-armhf
else
PLATFORM_DOWNLOAD_PATH=cli-alpine-$VSCODE_ARCH
fi
elif [ "$VSCODE_ARCH" = "arm64" ]; then
PLATFORM_DOWNLOAD_PATH=cli-darwin-arm64
else
PLATFORM_DOWNLOAD_PATH=cli-darwin-x64
fi
if [ ! -d "$VSCODE_AGENT_FOLDER" ]; then
mkdir -p "$VSCODE_AGENT_FOLDER"
chmod 750 "$VSCODE_AGENT_FOLDER"
error_code=$?
if [ "${error_code}" -gt 0 ]; then
echo "Creating the server install dir failed..."
fail_with_exitcode #<<ExitCode.CreateInstallDirFailed>>
fi
fi
#
# Delete old CLIs if needed
#
# shellcheck disable=SC2010
TO_DELETE=$(ls -1 -t "$VSCODE_AGENT_FOLDER" | grep -E "code(-insiders)?-[a-fA-F0-9]{40}" | tail -n +6)
for CLI_TO_DELETE in $TO_DELETE; do
target_dir="$VSCODE_AGENT_FOLDER/$CLI_TO_DELETE"
echo "Deleting old install $target_dir"
rm -rf "$target_dir"
done
do_client_download() {
DID_LOCAL_DOWNLOAD=1
echo "Trigger local server download"
echo "${GET_DOWNLOAD_SERVER_START_TRIGGER}"
echo artifact==$PLATFORM_DOWNLOAD_PATH==
echo destFolder==$VSCODE_AGENT_FOLDER==
echo destFolder2==/vscode-cli-$COMMIT_ID.tar.gz==
echo "${GET_DOWNLOAD_SERVER_END_TRIGGER}"
echo "Waiting for client to transfer server archive..."
echo "Waiting for $VSCODE_AGENT_FOLDER/vscode-cli-$COMMIT_ID.tar.gz.done and vscode-server.tar.gz to exist"
while true; do
if [ -f "$VSCODE_AGENT_FOLDER/vscode-cli-$COMMIT_ID.tar.gz.done" ]; then
if [ ! -f "$VSCODE_AGENT_FOLDER/vscode-cli-$COMMIT_ID.tar.gz" ]; then
echo "Found flag but not server tar - server transfer failed"
fail_with_exitcode #<<ExitCode.ServerTransferFailed>>
fi
echo "Found flag and server on host"
rm "$VSCODE_AGENT_FOLDER/vscode-cli-$COMMIT_ID.tar.gz.done"
break
else
printf ' '
sleep 3
fi
done
}
do_client_download_or_fail() {
error_code="${1:-"1"}"
if [ "$DID_LOCAL_DOWNLOAD" = "1" ]; then
echo "Already attempted local download, failing"
fail_with_exitcode "$error_code"
elif [ $ALLOW_CLIENT_DOWNLOAD = "1" ]; then
do_client_download
else
fail_with_exitcode "$error_code"
fi
}
is_program_from_busybox() {
program=$1
if command -v busybox > /dev/null 2>&1
then
# Check symlink from program to busybox
if [ -L "$(command -v "$program")" ] && [ "$(readlink -f "$(command -v "$program")")" = "$(command -v busybox)" ]
then
echo "Program '$program' is provided by busybox" >&2
echo 'yes'
return
fi
fi
echo 'no'
}
supports_flag() {
program="$1"
flag="$2"
if command -v "$program" > /dev/null 2>&1; then
if "$program" --help 2>&1 | grep -q -- "$flag"
then
echo "Program '$program' appears to support flag '$flag'" >&2
echo 'yes'
return
fi
fi
echo "Program '$program' is not available or does not appear to support flag '$flag'" >&2
echo 'no'
}
do_host_download() {
start=$(millis)
echo "${GET_PROGRESS_DOWNLOADING}"
UPDATE_URL="#<<updateUrl>>"
DOWNLOAD_URL=$UPDATE_URL/commit:$COMMIT_ID/$PLATFORM_DOWNLOAD_PATH/${QUALITY}
if command -v wget > /dev/null 2>&1
then
echo "Downloading with wget"
IS_WGET_BUSYBOX=$(is_program_from_busybox wget)
echo "wget is from busybox: $IS_WGET_BUSYBOX"
if [ "$IS_WGET_BUSYBOX" = 'no' ]
then
# Not busybox.
# Assuming its GNU wget or similar
if [ -n "$IGNORE_WGET_CONFIG_FLAG" ] && [ "$(supports_flag 'wget' '--no-config')" = "no" ]; then
echo "Detected that this version of wget does not support '--no-config'. Will not ignore wget default configuration files."
IGNORE_WGET_CONFIG_FLAG=""
fi
WGET_ERRORS=$(2>&1 wget ${IGNORE_WGET_CONFIG_FLAG} ${WGET_TRIES_SEGMENT} --connect-timeout=7 --dns-timeout=7 -nv -O vscode-cli-$COMMIT_ID.tar.gz ${DOWNLOAD_URL})
else
# Is busybox.
# Remove flags for the sake of compatibility
WGET_ERRORS=$(2>&1 wget -O vscode-cli-$COMMIT_ID.tar.gz ${DOWNLOAD_URL})
fi
error_code=$?
if [ $error_code -ne 0 ]
then
echo "wget download failed"
echo "$WGET_ERRORS"
printenv_indent
do_client_download_or_fail #<<ExitCode.ServerDownloadFailed>>
else
echo "Download complete"
DOWNLOAD_TIME=$(elapsed $start)
fi
else
command -v curl > /dev/null 2>&1
error_code=$?
if [ $error_code -eq 0 ]
then
echo "Downloading with curl"
CURL_STATUS_CODE_RESULT=$(curl ${IGNORE_CURL_CONFIG_FLAG} --connect-timeout 7 -L $DOWNLOAD_URL --output vscode-cli-$COMMIT_ID.tar.gz -w "%{http_code}")
error_code=$?
curl_status_code_if_2xx=$(echo "$CURL_STATUS_CODE_RESULT" | grep -E "^2[0-9]{2}$")
if [ $error_code -ne 0 ] || [ -z "$curl_status_code_if_2xx" ]
then
echo "curl download failed"
echo "HTTP status code: $CURL_STATUS_CODE_RESULT"
printenv_indent
do_client_download_or_fail #<<ExitCode.ServerDownloadFailed>>
else
echo "Download complete"
DOWNLOAD_TIME=$(elapsed $start)
fi
else
printenv_indent
echo "Neither wget nor curl is installed"
do_client_download_or_fail #<<ExitCode.NoDownloaderAvailable>>
fi
fi
}
do_install() {
start=$(millis)
echo "${GET_PROGRESS_INSTALLING}"
UNPACK_RESULT="#<<InstallUnpackCode.Success>>"
echo "tar --version: $(tar --version)"
tar -xf vscode-cli-$COMMIT_ID.tar.gz --no-same-owner
TAR_EXIT=$?
INSTALL_TIME=$(elapsed $start)
if [ $TAR_EXIT -ne 0 ]
then
echo "ERROR: tar exited with a non-zero exit code: $TAR_EXIT"
UNPACK_RESULT="#<<InstallUnpackCode.Error>>"
do_client_download_or_fail "#<<ExitCode.UnpackFailed>>"
do_install
return
fi
mv "$CLI_NAME_IN_ARCHIVE" "$CLI_PATH"
# cheap sanity check
if ! eval "$CLI_PATH --version"
then
UNPACK_RESULT="#<<InstallUnpackCode.MissingFiles>>"
echo "ERROR: $CLI_PATH don't exist"
do_client_download_or_fail "#<<ExitCode.UnpackFailed>>"
do_install
return
fi
# Delete the leftover folder which might have -web prefix, and the .tar.gz
rm -rf vscode-cli*
}
#
# Install if needed
#
if [ ! -f "$CLI_PATH" ]
then
echo "Installing to $VSCODE_AGENT_FOLDER..."
STASHED_WORKING_DIR="$(pwd)"
cd "$VSCODE_AGENT_FOLDER" || fail_with_exitcode #<<ExitCode.ChangeDirFailed>>
if [ $FORCE_CLIENT_DOWNLOAD = "1" ]; then
do_client_download
else
do_host_download
fi
do_install
cd "$STASHED_WORKING_DIR" || fail_with_exitcode #<<ExitCode.ChangeDirFailed>>
else
echo "Found existing installation at $VSCODE_AGENT_FOLDER..."
fi
#
# Start the server
#
start_server() {
echo "Starting VS Code CLI..."
printenv_indent
start=$(millis)
if [ -f $CLI_LOG_FILE ]; then
echo "Removing old logfile at $CLI_LOG_FILE"
rm "$CLI_LOG_FILE" # See #6265
fi
# Stop exporting VSCODE_AGENT_FOLDER once https://github.com/microsoft/vscode/pull/228287 is available and replace with:
# --extensions-dir "$VSCODE_AGENT_FOLDER/extensions" --user-data-dir "$VSCODE_AGENT_FOLDER/data"
# See https://github.com/microsoft/vscode-internalbacklog/issues/2604, https://github.com/microsoft/vscode-remote-release/issues/10255
export VSCODE_AGENT_FOLDER
touch $CLI_LOG_FILE
chmod 600 $CLI_LOG_FILE
LOG_LEVEL_FLAG=""
if [ "$VERBOSE" = "1" ]; then
echo "Setting '--log trace' on CLI"
LOG_LEVEL_FLAG="--log trace"
fi
RECONNECTION_FLAG=""
if [ -n "$RECONNECTION_GRACE_TIME" ]; then
RECONNECTION_FLAG="--reconnection-grace-time $RECONNECTION_GRACE_TIME"
fi
VSCODE_CLI_REQUIRE_TOKEN=${TOKEN} "$CLI_PATH" command-shell ${LOG_LEVEL_FLAG} ${RECONNECTION_FLAG} --cli-data-dir "$VSCODE_AGENT_FOLDER/cli" --parent-process-id $$ ${LISTEN_ARGS} > "$CLI_LOG_FILE" 2>&1 < /dev/null &
CLI_PID=$!
echo "Spawned remote CLI: $!"
count=0
max_retries=15
while [ $count -lt $max_retries ]; do
count=$((count + 1))
LISTENING_ON=$(cat "$CLI_LOG_FILE" | grep -a -E 'Listening on .+' | grep -v grep | sed 's/Listening on //')
if [ "$LISTENING_ON" != '' ]
then
break
fi
# "If sig is 0 (the null signal), error checking is performed but no signal is actually sent.
# The null signal can be used to check the validity of pid.""
# Source: https://pubs.opengroup.org/onlinepubs/007908799/xsh/kill.html
if ! kill -0 $CLI_PID > /dev/null; then
echo "Exec server process not found"
cat $CLI_LOG_FILE
if grep -q "This machine does not meet .* prerequisites, expected either..." "$CLI_LOG_FILE"; then
fail_with_exitcode #<<ExitCode.LinuxPrereqs>>
fi
break
fi
echo "Waiting for server log..."
sleep .03 || sleep 1
done
SERVER_START_TIME=$(elapsed $start)
}
start_server
# What we echo below cannot be wider than 80 characters
echo "${UUID}: start"
echo_common_results
echo "${UUID}: end"
while true; do sleep 180; printf ' '; done

View File

@@ -0,0 +1,13 @@
@ECHO OFF
setlocal EnableExtensions
rem get unique file name
:uniqLoop
set VSCODE_SSH_ASKPASS_RESULT=%tmp%\vscode-ssh-askpass-result-%RANDOM%.tmp
if exist "%VSCODE_SSH_ASKPASS_RESULT%" goto :uniqLoop
"%VSCODE_SSH_ASKPASS_NODE%" "%VSCODE_SSH_ASKPASS_MAIN%" "%VSCODE_SSH_ASKPASS_EXTRA_ARGS%" %*
if exist %VSCODE_SSH_ASKPASS_RESULT% (
type "%VSCODE_SSH_ASKPASS_RESULT%""
del %VSCODE_SSH_ASKPASS_RESULT%
)

View File

@@ -0,0 +1,6 @@
#!/bin/sh
VSCODE_SSH_ASKPASS_RESULT=`mktemp`
VSCODE_SSH_ASKPASS_RESULT="$VSCODE_SSH_ASKPASS_RESULT" "$VSCODE_SSH_ASKPASS_NODE" "$VSCODE_SSH_ASKPASS_MAIN" "$VSCODE_SSH_ASKPASS_EXTRA_ARGS" "$*"
cat $VSCODE_SSH_ASKPASS_RESULT
rm $VSCODE_SSH_ASKPASS_RESULT

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,456 @@
"use strict";
exports.id = "node_modules_node-fetch_src_utils_multipart-parser_js";
exports.ids = ["node_modules_node-fetch_src_utils_multipart-parser_js"];
exports.modules = {
/***/ "./node_modules/node-fetch/src/utils/multipart-parser.js":
/*!***************************************************************!*\
!*** ./node_modules/node-fetch/src/utils/multipart-parser.js ***!
\***************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ toFormData: () => (/* binding */ toFormData)
/* harmony export */ });
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! fetch-blob/from.js */ "./node_modules/fetch-blob/from.js");
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! formdata-polyfill/esm.min.js */ "./node_modules/formdata-polyfill/esm.min.js");
let s = 0;
const S = {
START_BOUNDARY: s++,
HEADER_FIELD_START: s++,
HEADER_FIELD: s++,
HEADER_VALUE_START: s++,
HEADER_VALUE: s++,
HEADER_VALUE_ALMOST_DONE: s++,
HEADERS_ALMOST_DONE: s++,
PART_DATA_START: s++,
PART_DATA: s++,
END: s++
};
let f = 1;
const F = {
PART_BOUNDARY: f,
LAST_BOUNDARY: f *= 2
};
const LF = 10;
const CR = 13;
const SPACE = 32;
const HYPHEN = 45;
const COLON = 58;
const A = 97;
const Z = 122;
const lower = c => c | 0x20;
const noop = () => {};
class MultipartParser {
/**
* @param {string} boundary
*/
constructor(boundary) {
this.index = 0;
this.flags = 0;
this.onHeaderEnd = noop;
this.onHeaderField = noop;
this.onHeadersEnd = noop;
this.onHeaderValue = noop;
this.onPartBegin = noop;
this.onPartData = noop;
this.onPartEnd = noop;
this.boundaryChars = {};
boundary = '\r\n--' + boundary;
const ui8a = new Uint8Array(boundary.length);
for (let i = 0; i < boundary.length; i++) {
ui8a[i] = boundary.charCodeAt(i);
this.boundaryChars[ui8a[i]] = true;
}
this.boundary = ui8a;
this.lookbehind = new Uint8Array(this.boundary.length + 8);
this.state = S.START_BOUNDARY;
}
/**
* @param {Uint8Array} data
*/
write(data) {
let i = 0;
const length_ = data.length;
let previousIndex = this.index;
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
const boundaryLength = this.boundary.length;
const boundaryEnd = boundaryLength - 1;
const bufferLength = data.length;
let c;
let cl;
const mark = name => {
this[name + 'Mark'] = i;
};
const clear = name => {
delete this[name + 'Mark'];
};
const callback = (callbackSymbol, start, end, ui8a) => {
if (start === undefined || start !== end) {
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
}
};
const dataCallback = (name, clear) => {
const markSymbol = name + 'Mark';
if (!(markSymbol in this)) {
return;
}
if (clear) {
callback(name, this[markSymbol], i, data);
delete this[markSymbol];
} else {
callback(name, this[markSymbol], data.length, data);
this[markSymbol] = 0;
}
};
for (i = 0; i < length_; i++) {
c = data[i];
switch (state) {
case S.START_BOUNDARY:
if (index === boundary.length - 2) {
if (c === HYPHEN) {
flags |= F.LAST_BOUNDARY;
} else if (c !== CR) {
return;
}
index++;
break;
} else if (index - 1 === boundary.length - 2) {
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
state = S.END;
flags = 0;
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
index = 0;
callback('onPartBegin');
state = S.HEADER_FIELD_START;
} else {
return;
}
break;
}
if (c !== boundary[index + 2]) {
index = -2;
}
if (c === boundary[index + 2]) {
index++;
}
break;
case S.HEADER_FIELD_START:
state = S.HEADER_FIELD;
mark('onHeaderField');
index = 0;
// falls through
case S.HEADER_FIELD:
if (c === CR) {
clear('onHeaderField');
state = S.HEADERS_ALMOST_DONE;
break;
}
index++;
if (c === HYPHEN) {
break;
}
if (c === COLON) {
if (index === 1) {
// empty header field
return;
}
dataCallback('onHeaderField', true);
state = S.HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
return;
}
break;
case S.HEADER_VALUE_START:
if (c === SPACE) {
break;
}
mark('onHeaderValue');
state = S.HEADER_VALUE;
// falls through
case S.HEADER_VALUE:
if (c === CR) {
dataCallback('onHeaderValue', true);
callback('onHeaderEnd');
state = S.HEADER_VALUE_ALMOST_DONE;
}
break;
case S.HEADER_VALUE_ALMOST_DONE:
if (c !== LF) {
return;
}
state = S.HEADER_FIELD_START;
break;
case S.HEADERS_ALMOST_DONE:
if (c !== LF) {
return;
}
callback('onHeadersEnd');
state = S.PART_DATA_START;
break;
case S.PART_DATA_START:
state = S.PART_DATA;
mark('onPartData');
// falls through
case S.PART_DATA:
previousIndex = index;
if (index === 0) {
// boyer-moore derrived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < bufferLength && !(data[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = data[i];
}
if (index < boundary.length) {
if (boundary[index] === c) {
if (index === 0) {
dataCallback('onPartData', true);
}
index++;
} else {
index = 0;
}
} else if (index === boundary.length) {
index++;
if (c === CR) {
// CR = part boundary
flags |= F.PART_BOUNDARY;
} else if (c === HYPHEN) {
// HYPHEN = end boundary
flags |= F.LAST_BOUNDARY;
} else {
index = 0;
}
} else if (index - 1 === boundary.length) {
if (flags & F.PART_BOUNDARY) {
index = 0;
if (c === LF) {
// unset the PART_BOUNDARY flag
flags &= ~F.PART_BOUNDARY;
callback('onPartEnd');
callback('onPartBegin');
state = S.HEADER_FIELD_START;
break;
}
} else if (flags & F.LAST_BOUNDARY) {
if (c === HYPHEN) {
callback('onPartEnd');
state = S.END;
flags = 0;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index - 1] = c;
} else if (previousIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
callback('onPartData', 0, previousIndex, _lookbehind);
previousIndex = 0;
mark('onPartData');
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case S.END:
break;
default:
throw new Error(`Unexpected state entered: ${state}`);
}
}
dataCallback('onHeaderField');
dataCallback('onHeaderValue');
dataCallback('onPartData');
// Update properties for the next call
this.index = index;
this.state = state;
this.flags = flags;
}
end() {
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
this.onPartEnd();
} else if (this.state !== S.END) {
throw new Error('MultipartParser.end(): stream ended unexpectedly');
}
}
}
function _fileName(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
if (!m) {
return;
}
const match = m[2] || m[3] || '';
let filename = match.slice(match.lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
return String.fromCharCode(code);
});
return filename;
}
async function toFormData(Body, ct) {
if (!/multipart/i.test(ct)) {
throw new TypeError('Failed to fetch');
}
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
if (!m) {
throw new TypeError('no or bad content-type header, no multipart boundary');
}
const parser = new MultipartParser(m[1] || m[2]);
let headerField;
let headerValue;
let entryValue;
let entryName;
let contentType;
let filename;
const entryChunks = [];
const formData = new formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__.FormData();
const onPartData = ui8a => {
entryValue += decoder.decode(ui8a, {stream: true});
};
const appendToFile = ui8a => {
entryChunks.push(ui8a);
};
const appendFileToFormData = () => {
const file = new fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__.File(entryChunks, filename, {type: contentType});
formData.append(entryName, file);
};
const appendEntryToFormData = () => {
formData.append(entryName, entryValue);
};
const decoder = new TextDecoder('utf-8');
decoder.decode();
parser.onPartBegin = function () {
parser.onPartData = onPartData;
parser.onPartEnd = appendEntryToFormData;
headerField = '';
headerValue = '';
entryValue = '';
entryName = '';
contentType = '';
filename = null;
entryChunks.length = 0;
};
parser.onHeaderField = function (ui8a) {
headerField += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderValue = function (ui8a) {
headerValue += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderEnd = function () {
headerValue += decoder.decode();
headerField = headerField.toLowerCase();
if (headerField === 'content-disposition') {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
if (m) {
entryName = m[2] || m[3] || '';
}
filename = _fileName(headerValue);
if (filename) {
parser.onPartData = appendToFile;
parser.onPartEnd = appendFileToFormData;
}
} else if (headerField === 'content-type') {
contentType = headerValue;
}
headerValue = '';
headerField = '';
};
for await (const chunk of Body) {
parser.write(chunk);
}
parser.end();
return formData;
}
/***/ })
};
;
//# sourceMappingURL=node_modules_node-fetch_src_utils_multipart-parser_js.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */

View File

@@ -0,0 +1 @@
/*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */

View File

@@ -0,0 +1,13 @@
#!/bin/bash
terminateTree() {
for cpid in $(/usr/bin/pgrep -P $1); do
terminateTree $cpid
done
kill -9 $1 > /dev/null 2>&1
}
# shellcheck disable=SC2048
for pid in $*; do
terminateTree $pid
done

View File

@@ -0,0 +1,618 @@
{
"name": "remote-ssh",
"displayName": "Remote - SSH",
"description": "%description%",
"version": "0.122.0",
"publisher": "ms-vscode-remote",
"icon": "resources/remote-ssh.png",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-remote-release.git"
},
"bugs": {
"url": "https://github.com/Microsoft/vscode-remote-release/issues"
},
"license": "SEE LICENSE IN LICENSE.txt",
"engines": {
"vscode": "^1.107.0"
},
"extensionKind": [
"ui"
],
"enabledApiProposals": [
"resolvers",
"tunnels",
"terminalDataWriteEvent",
"contribViewsRemote",
"telemetry",
"contribRemoteHelp"
],
"extensionPack": [
"ms-vscode-remote.remote-ssh-edit",
"ms-vscode.remote-explorer"
],
"categories": [
"Other"
],
"keywords": [
"remote development",
"remote",
"ssh"
],
"api": "none",
"activationEvents": [
"onCommand:remote-internal.getSshFoldersHistory",
"onCommand:remote-internal.deleteSshFolderFromHistory",
"onCommand:remote-internal.getActiveSshRemote",
"onCommand:remote-internal.getConfiguredHostnames",
"onCommand:remote-internal.openRemoteSshTarget",
"onResolveRemoteAuthority:ssh-remote",
"onUri"
],
"l10n": "./l10n",
"main": "./out/extension",
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
"capabilities": {
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"chatParticipants": [
{
"name": "remote-ssh",
"fullName": "Remote - SSH",
"id": "remote-ssh",
"description": "%chatParticipants.description%",
"isSticky": true,
"when": "config.remote.SSH.experimental.chat"
}
],
"remoteHelp": {
"getStarted": "https://aka.ms/vscode-remote/ssh/getting-started",
"feedback": "https://aka.ms/vscode-remote/ssh/provide-feedback",
"documentation": "https://aka.ms/vscode-remote/ssh",
"issues": "https://aka.ms/vscode-remote/ssh/issues",
"remoteName": [
"ssh-remote"
]
},
"configuration": {
"title": "Remote - SSH",
"properties": {
"remote.SSH.configFile": {
"type": "string",
"description": "%setting.configFile.description%",
"default": "",
"scope": "application"
},
"remote.SSH.showLoginTerminal": {
"type": "boolean",
"description": "%setting.showLoginTerminal.description%",
"scope": "application",
"default": false
},
"remote.SSH.defaultForwardedPorts": {
"type": "array",
"items": {
"type": "object",
"required": [
"remotePort"
],
"properties": {
"name": {
"type": "string"
},
"remotePort": {
"type": "number"
},
"localPort": {
"type": "number"
}
}
},
"description": "%setting.defaultForwardedPorts.description%",
"default": []
},
"remote.SSH.defaultExtensions": {
"type": "array",
"items": {
"type": "string"
},
"description": "%setting.defaultExtensions.description%",
"scope": "application"
},
"remote.SSH.path": {
"type": "string",
"description": "%setting.path.description%",
"default": "",
"scope": "application"
},
"remote.SSH.serverInstallPath": {
"type": "object",
"description": "%setting.serverInstallPath.description%",
"scope": "application",
"additionalProperties": {
"type": "string"
}
},
"remote.SSH.lockfilesInTmp": {
"type": "boolean",
"markdownDescription": "%setting.lockfilesInTmp.description%",
"scope": "application",
"default": false
},
"remote.SSH.useFlock": {
"type": "boolean",
"markdownDescription": "%setting.useFlock.description%",
"scope": "application",
"default": true
},
"remote.SSH.enableDynamicForwarding": {
"type": "boolean",
"description": "%setting.enableDynamicForwarding.description%",
"scope": "application",
"default": true
},
"remote.SSH.enableAgentForwarding": {
"type": "boolean",
"markdownDescription": "%setting.enableAgentForwarding.description%",
"scope": "application",
"default": true
},
"remote.SSH.enableX11Forwarding": {
"type": "boolean",
"markdownDescription": "%setting.enableX11Forwarding.description%",
"scope": "application",
"default": true
},
"remote.SSH.permitPtyAllocation": {
"type": "boolean",
"markdownDescription": "%setting.permitPtyAllocation.description%",
"scope": "application",
"default": false
},
"remote.SSH.useCurlAndWgetConfigurationFiles": {
"type": "boolean",
"markdownDescription": "%setting.useCurlAndWgetConfigurationFiles.description%",
"scope": "application",
"default": false
},
"remote.SSH.useExecServer": {
"type": "boolean",
"markdownDescription": "%setting.useExecServer.description%",
"scope": "application",
"default": true
},
"remote.SSH.enableRemoteCommand": {
"type": "boolean",
"markdownDescription": "%setting.enableRemoteCommand.description%",
"scope": "application",
"default": false
},
"remote.SSH.externalSSH_ASKPASS": {
"type": "boolean",
"markdownDescription": "%setting.externalSSH_ASKPASS.description%",
"scope": "application",
"default": false
},
"remote.SSH.allowLocalServerDownload": {
"type": "boolean",
"description": "%setting.allowLocalServerDownload.description%",
"markdownDeprecationMessage": "%setting.allowLocalServerDownload.deprecationMessage%",
"scope": "application",
"default": true
},
"remote.SSH.localServerDownload": {
"type": "string",
"description": "%setting.localServerDownload.description%",
"enum": [
"auto",
"always",
"off"
],
"markdownEnumDescriptions": [
"%setting.localServerDownload.auto.description%",
"%setting.localServerDownload.always.description%",
"%setting.localServerDownload.off.description%"
],
"scope": "application",
"default": "auto"
},
"remote.SSH.windowsRemotes": {
"type": "array",
"markdownDescription": "%setting.windowsRemotes.description%",
"deprecationMessage": "%setting.windowsRemotes.deprecationMessage%",
"default": [],
"items": {
"type": "string"
},
"scope": "application"
},
"remote.SSH.useLocalServer": {
"type": "boolean",
"markdownDescription": "%setting.useLocalServer.description%",
"scope": "application",
"default": true
},
"remote.SSH.connectTimeout": {
"type": "number",
"description": "%setting.connectTimeout.description%",
"default": 15,
"scope": "application",
"minimum": 1
},
"remote.SSH.logLevel": {
"type": "string",
"description": "%setting.logLevel.description%",
"default": "debug",
"scope": "application",
"enum": [
"debug",
"trace"
]
},
"remote.SSH.maxReconnectionAttempts": {
"type": [
"number",
"null"
],
"markdownDescription": "%setting.maxReconnectionAttempts.description%",
"default": null,
"scope": "application",
"maximum": 8,
"minimum": 0
},
"remote.SSH.suppressWindowsSshWarning": {
"type": "boolean",
"description": "%setting.suppressWindowsSshWarning.description%",
"default": false,
"scope": "application"
},
"remote.SSH.remotePlatform": {
"type": "object",
"markdownDescription": "%setting.remotePlatform.description%",
"scope": "application",
"additionalProperties": {
"type": "string",
"enum": [
"macOS",
"windows",
"linux"
]
}
},
"remote.SSH.remoteServerListenOnSocket": {
"type": "boolean",
"default": false,
"markdownDescription": "%setting.remoteServerListenOnSocket.description%"
},
"remote.SSH.serverPickPortsFromRange": {
"type": "object",
"markdownDescription": "%setting.serverPickPortsFromRange.description%",
"scope": "application",
"additionalProperties": {
"type": "string",
"pattern": "^\\d+-\\d+$"
}
},
"remote.SSH.preferredLocalPortRange": {
"type": "string",
"default": "",
"markdownDescription": "%setting.preferredLocalPortRange.description%",
"scope": "application",
"pattern": "^\\d+-\\d+$|^$"
},
"remote.SSH.bindHost": {
"type": "object",
"markdownDescription": "%setting.bindHost.description%",
"scope": "application",
"examples": [
{
"example.com": "127.0.0.1"
}
],
"additionalProperties": {
"type": "string",
"default": "127.0.0.1"
}
},
"remote.SSH.experimental.chat": {
"type": "boolean",
"markdownDescription": "%setting.experimental.chat.description%",
"scope": "application",
"default": true,
"tags": [
"experimental"
]
},
"remote.SSH.experimental.enhancedSessionLogs": {
"type": "boolean",
"markdownDescription": "%setting.experimental.enhancedSessionLogs.description%",
"scope": "application",
"default": true,
"tags": [
"experimental"
]
},
"remote.SSH.httpProxy": {
"type": [
"string",
"object"
],
"markdownDescription": "%setting.httpProxy.description%",
"additionalProperties": {
"type": "string"
},
"default": "",
"examples": [
{
"myhost": "http://proxy.example.com:8080",
"myhost2": "http://proxy2.example.com"
},
"http://proxy.example.com:8080"
]
},
"remote.SSH.httpsProxy": {
"type": [
"string",
"object"
],
"markdownDescription": "%setting.httpsProxy.description%",
"additionalProperties": {
"type": "string"
},
"default": "",
"examples": [
{
"myhost": "https://proxy.example.com:8080",
"myhost2": "https://proxy2.example.com"
},
"https://proxy.example.com:8080"
]
},
"remote.SSH.preconnect": {
"type": [
"string",
"object"
],
"markdownDescription": "%setting.preconnect.description%",
"additionalProperties": {
"type": "string"
},
"default": "",
"examples": [
{
"myhost": "~/preconnect.sh",
"myhost2": "C:\\Users\\user\\preconnect.ps1"
},
"~/preconnect.sh"
],
"tags": [
"experimental"
]
},
"remote.SSH.reconnectionGraceTime": {
"type": [
"number",
"null"
],
"scope": "application",
"markdownDescription": "%setting.reconnectionGraceTime.description%",
"default": null
}
}
},
"commands": [
{
"command": "opensshremotes.openWebUI",
"title": "%commands.openWebUI.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.openEmptyWindow",
"title": "%commands.openEmptyWindow,title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.openEmptyWindowOnCurrentHost",
"title": "%commands.openEmptyWindowOnCurrentHost.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.openEmptyWindowInCurrentWindow",
"title": "%commands.openEmptyWindowInCurrentWindow.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.openConfigFile",
"title": "%commands.openConfigFile.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.addNewSshHost",
"title": "%commands.addNewSshHost.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.cleanDevBox",
"title": "%commands.cleanDevBox.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.cleanRemoteServer",
"title": "%commands.cleanRemoteServer.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.cleanCurrentRemoteServer",
"title": "%commands.cleanCurrentRemoteServer.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.cleanLocalServer",
"title": "%commands.cleanLocalServer.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.getHelp",
"title": "%commands.getHelp.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.getStarted",
"title": "%commands.getStarted.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.reportIssue",
"title": "%commands.reportIssue.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.settings",
"title": "%commands.settings.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.provideFeedback",
"title": "%commands.provideFeedback.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.showLog",
"title": "%commands.showLog.title%",
"category": "Remote-SSH"
},
{
"command": "opensshremotes.clearAllSessionHints",
"title": "%commands.clearAllSessionHints.title%",
"category": "Remote-SSH",
"enablement": "config.remote.SSH.experimental.chat"
}
],
"resourceLabelFormatters": [
{
"scheme": "vscode-remote",
"authority": "ssh-remote+*",
"formatting": {
"label": "${path}",
"separator": "/",
"tildify": true,
"workspaceSuffix": "SSH"
}
}
],
"menus": {
"statusBar/remoteIndicator": [
{
"command": "opensshremotes.openEmptyWindow",
"group": "remote_20_ssh-remote_1general@1"
},
{
"command": "opensshremotes.openEmptyWindowInCurrentWindow",
"group": "remote_20_ssh-remote_1general@2"
}
],
"commandPalette": [
{
"command": "opensshremotes.getStarted",
"when": "!remoteName && !virtualWorkspace || remoteName =~ /^ssh-remote$/ && remoteConnectionState == disconnected"
},
{
"command": "opensshremotes.openEmptyWindowOnCurrentHost",
"when": "remoteName =~ /^ssh-remote$/ && remoteConnectionState == connected"
},
{
"command": "opensshremotes.provideFeedback",
"when": "remoteName =~ /^ssh-remote$/"
},
{
"command": "opensshremotes.cleanCurrentRemoteServer",
"when": "remoteName =~ /^ssh-remote$/ && remoteConnectionState == connected"
},
{
"command": "opensshremotes.cleanLocalServer",
"when": "config.remote.SSH.useLocalServer"
},
{
"command": "opensshremotes.openWebUI",
"when": "remoteName =~ /^ssh-remote$/ && remoteConnectionState == connected && config.remote.SSH.enableWebAccess"
}
]
}
},
"scripts": {
"lint:eslint": "eslint \"src/**/*.ts\"",
"lint:shellcheck": "node scripts/shellcheck.js",
"lint": "npm-run-all lint:eslint lint:shellcheck",
"preinstall": "npm_config_registry=https://registry.npmjs.org npm exec ado-npm-auth",
"postinstall": "npm run downloadapi",
"downloadapi": "vscode-dts dev",
"vscode:prepublish": "webpack --mode production",
"compile": "webpack --mode development",
"watch": "webpack --mode development --watch",
"package": "vsce package",
"pretest": "npm run compile",
"test": "vscode-test --label unit",
"test:baseline": "vscode-test --label baseline",
"baseline": "npm-run-all pretest test:baseline"
},
"devDependencies": {
"@devcontainers/cli": "latest",
"@types/glob": "^8.1.0",
"@types/lockfile": "^1.0.4",
"@types/minimist": "^1.2.5",
"@types/mocha": "^10.0.6",
"@types/node": "^20.10.3",
"@types/shell-quote": "^1.7.5",
"@types/sinon": "^17.0.2",
"@types/vscode": "1.90.0",
"@types/webpack": "^5.28.5",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
"@vscode/vsce": "^2.22.0",
"ado-npm-auth": "^0.1.2",
"copy-webpack-plugin": "^11.0.0",
"copyfiles": "^2.4.1",
"eslint": "^8.55.0",
"mocha": "^10.2.0",
"npm-run-all2": "^7.0.1",
"raw-loader": "^4.0.2",
"shellcheck": "^3.0.0",
"sinon": "^17.0.1",
"ts-loader": "^9.5.1",
"ts-sinon": "^2.0.2",
"typescript": "^5.3.2",
"vscode-dts": "^0.3.3",
"webpack": "^5.94.0",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"@enonic/fnv-plus": "^1.3.0",
"@microsoft/dev-tunnels-contracts": "^1.0.7463",
"@vscode-internal/remote-web-rpc": "^2.1.9",
"@vscode/chat-extension-utils": "^0.0.0-alpha.4",
"@vscode/extension-telemetry": "^0.9.0",
"@vscode/prompt-tsx": "^0.3.0-alpha.13",
"glob": "^10.5.0",
"https-proxy-agent": "^7.0.2",
"lockfile": "^1.0.4",
"minimist": "^1.2.8",
"posix-getopt": "^1.2.1",
"shell-quote": "^1.8.1",
"socks": "^2.7.3",
"ssh-config": "^5.0.2",
"table": "^6.8.2"
},
"resolutions": {
"strip-ansi": "6.0.1"
},
"__metadata": {
"installedTimestamp": 1765760155775,
"targetPlatform": "undefined",
"size": 2831883
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More