Skip to content

Commit 1141b33

Browse files
committed
Merge pull request #373
2 parents 5c1a874 + b975c9d commit 1141b33

File tree

7 files changed

+34
-6
lines changed

7 files changed

+34
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
## Unreleased
55

6+
### Changes
7+
8+
- Added an `Encoding` terminal character encoding option to SSH FS configs (#431 by [@minoru-nagasawa](https://github.com/minoru-nagasawa))
9+
610
### Major change
711

812
- Update from patched ssh2@^1.11.0 to ssh@^1.16.0

common/src/fileSystemConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ export interface FileSystemConfig extends ConnectConfig {
117117
instantConnection?: boolean;
118118
/** List of special flags to enable/disable certain fixes/features. Flags are usually used for issues or beta testing. Flags can disappear/change anytime! */
119119
flags?: string[];
120+
/** Specifies the character encoding used for the SSH terminal. If undefined, UTF-8 will be used */
121+
encoding?: string;
120122
/** Internal property saying where this config comes from. Undefined if this config is merged or something */
121123
_location?: ConfigLocation;
122124
/** Internal property keeping track of where this config comes from (including merges) */

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@
416416
"dependencies": {
417417
"common": "workspace:*",
418418
"event-stream": "^4.0.1",
419+
"iconv-lite": "^0.6.3",
419420
"jsonc-parser": "^3.2.0",
420421
"semver": "^7.3.5",
421422
"socks": "^2.2.0",

src/pseudoTerminal.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type { EnvironmentVariable, FileSystemConfig } from 'common/fileSystemCon
22
import * as path from 'path';
33
import type { ClientChannel, PseudoTtyOptions } from 'ssh2';
44
import * as vscode from 'vscode';
5-
import { getFlagBoolean } from './flags';
65
import type { Connection } from './connection';
6+
import { getFlagBoolean } from './flags';
77
import { Logging, LOGGING_NO_STACKTRACE } from './logging';
88
import { environmentToExportString, joinCommands, mergeEnvironment, toPromise } from './utils';
99

@@ -135,13 +135,26 @@ export async function replaceVariablesRecursive<T>(object: T, handler: (value: s
135135
return object;
136136
}
137137

138+
async function getEncodingHandlers(encoding?: string): Promise<[encode: (data: string) => Buffer, decode: (data: Buffer) => string]> {
139+
if (encoding) {
140+
const iconv = await import('iconv-lite');
141+
if (!iconv.encodingExists(encoding))
142+
throw new Error(`Unknown character encoding '${encoding}'`);
143+
return [data => iconv.encode(data, encoding), data => iconv.decode(data, encoding)];
144+
}
145+
return [data => Buffer.from(data, 'utf-8'), data => data.toString('utf-8')];
146+
}
147+
138148
export async function createTerminal(options: TerminalOptions): Promise<SSHPseudoTerminal> {
139149
const { connection } = options;
140150
const { actualConfig, client, shellConfig } = connection;
141151
const onDidWrite = new vscode.EventEmitter<string>();
142152
const onDidClose = new vscode.EventEmitter<number>();
143153
const onDidOpen = new vscode.EventEmitter<void>();
144154
let terminal: vscode.Terminal | undefined;
155+
156+
const [encodeInput, decodeOutput] = await getEncodingHandlers(actualConfig.encoding);
157+
145158
// Won't actually open the remote terminal until pseudo.open(dims) is called
146159
const pseudo: SSHPseudoTerminal = {
147160
status: 'opening',
@@ -241,8 +254,8 @@ export async function createTerminal(options: TerminalOptions): Promise<SSHPseud
241254
if (pseudo.status === 'opening') pseudo.status = 'open';
242255
onDidOpen.fire();
243256
});
244-
channel.on('data', chunk => onDidWrite.fire(chunk.toString()));
245-
channel.stderr!.on('data', chunk => onDidWrite.fire(chunk.toString()));
257+
channel.on('data', chunk => onDidWrite.fire(decodeOutput(chunk)));
258+
channel.stderr!.on('data', chunk => onDidWrite.fire(decodeOutput(chunk)));
246259
// TODO: ^ Keep track of stdout's color, switch to red, output, then switch back?
247260
} catch (e) {
248261
Logging.error`Error starting SSH terminal:\n${e}`;
@@ -264,7 +277,7 @@ export async function createTerminal(options: TerminalOptions): Promise<SSHPseud
264277
},
265278
handleInput(data) {
266279
if (pseudo.status === 'wait-to-close') return pseudo.close();
267-
pseudo.channel?.write(data);
280+
pseudo.channel?.write(encodeInput(data));
268281
},
269282
};
270283
return pseudo;

webview/src/ConfigEditor/fields.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,16 @@ export function taskCommand(config: FileSystemConfig, onChange: FSCChanged<'task
169169
return <FieldDropdownWithInput key="taskCommand" label="Task command" {...{ value, values, description }} onChange={callback} optional />
170170
}
171171

172+
export function encoding(config: FileSystemConfig, onChange: FSCChanged<'encoding'>): React.ReactElement {
173+
const callback = (newValue?: string) => onChange('encoding', newValue);
174+
const description = (<>Text encoding used for terminal input/output. For a list of supported encodings, see <a href="https://github.com/ashtuchkin/iconv-lite/wiki/Supported-Encodings" target="_blank" rel="noreferrer">iconv-lite wiki</a></>);
175+
const values = ['utf8', 'iso-8859-1', 'Shift_JIS', 'EUC-JP', 'EUC-KR'];
176+
return <FieldDropdownWithInput key="encoding" label="Encoding" {...{ value: config.encoding, values, description }} onChange={callback} optional />
177+
}
178+
172179
export type FieldFactory = (config: FileSystemConfig, onChange: FSCChanged, onChangeMultiple: FSCChangedMultiple) => React.ReactElement | null;
173180
export const FIELDS: FieldFactory[] = [
174181
name, label, group, merge, extend, putty, host, port,
175182
root, agent, username, password, privateKeyPath, passphrase,
176-
newFileMode, agentForward, sftpCommand, sftpSudo, terminalCommand, taskCommand,
183+
newFileMode, agentForward, sftpCommand, sftpSudo, terminalCommand, taskCommand, encoding,
177184
PROXY_FIELD];

webview/src/FieldTypes/base.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import './index.css';
44

55
export interface Props<T> {
66
label?: string;
7-
description?: string;
7+
description?: React.ReactNode;
88
value: T;
99
optional?: boolean;
1010
group?: FieldGroup;

yarn.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10317,6 +10317,7 @@ __metadata:
1031710317
"@vscode/vsce": "npm:^2.18.0"
1031810318
common: "workspace:*"
1031910319
event-stream: "npm:^4.0.1"
10320+
iconv-lite: "npm:^0.6.3"
1032010321
jsonc-parser: "npm:^3.2.0"
1032110322
prettier: "npm:^2.6.2"
1032210323
semver: "npm:^7.3.5"

0 commit comments

Comments
 (0)