Last active
October 29, 2025 12:22
-
-
Save Ishotihadus/8933ab0b2c347dbbe2a2b2f070d3d50e to your computer and use it in GitHub Desktop.
Windows 版 VSCode のショートカットキーを Mac 版とほぼ同じにするスクリプト 生成された keybindings.json を追加する 詳細: https://note.com/ishotihadus/n/ne1171989949a
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # frozen_string_literal: true | |
| require 'json' | |
| require 'net/http' | |
| @key_map = {} | |
| # ターミナル用に機械的に生成したもの | |
| @ascii_arrays = [['shift', '2'], ['a'], ['b'], ['c'], ['d'], ['e'], ['f'], ['g'], ['h'], ['i'], ['j'], ['k'], ['l'], | |
| ['m'], ['n'], ['o'], ['p'], ['q'], ['r'], ['s'], ['t'], ['u'], ['v'], ['w'], ['x'], ['y'], ['z'], | |
| ['['], ['\\'], [']'], ['shift', '6'], ['shift', '-']] | |
| @ascii_arrays.map.with_index do |k, i| | |
| a = %w[ctrl alt shift][i / 12] | |
| b = "f#{13 + (i % 12)}" | |
| @key_map[Set['xxxx'] | Set[*k]] = Set[a, b] | |
| end | |
| @ascii_arrays_shift = [nil, | |
| %w[shift a], %w[shift b], %w[shift c], %w[shift d], %w[shift e], %w[shift f], %w[shift g], | |
| %w[shift h], %w[shift i], %w[shift j], %w[shift k], %w[shift l], %w[shift m], %w[shift n], | |
| %w[shift o], %w[shift p], %w[shift q], %w[shift r], %w[shift s], %w[shift t], %w[shift u], | |
| %w[shift v], %w[shift w], %w[shift x], %w[shift y], %w[shift z], | |
| ['shift', '['], ['shift', '\\'], ['shift', ']']] | |
| @ascii_arrays_shift.map.with_index do |k, i| | |
| a = [%w[ctrl shift], %w[alt shift], %w[ctrl alt]][i / 12] | |
| b = "f#{13 + (i % 12)}" | |
| @key_map[Set['xxxx'] | Set[*k]] = Set[a, b] | |
| end | |
| # 手動マッピング | |
| @key_map[Set['xxxx', 'space']] = Set['shift', 'f23'] | |
| @key_map[Set['xxxx', 'backspace']] = Set['shift', 'f24'] | |
| (0..9).each do |n| | |
| @key_map[Set['xxxx', n.to_s]] = Set['alt', n.to_s] | |
| end | |
| # Mac 用のキーを Windows 用に変換する | |
| def replace(keys) | |
| keys = keys.map do |key| | |
| next 'xxxx' if key == 'ctrl' | |
| next 'ctrl' if key == 'cmd' | |
| key | |
| end | |
| keys = Set.new(keys) | |
| return @key_map[keys] if @key_map.key?(keys) | |
| return keys unless keys.include?('xxxx') | |
| nil | |
| end | |
| # 読み込み | |
| macos = JSON.parse(Net::HTTP.get(URI("https://raw.githubusercontent.com/codebling/vs-code-default-keybindings/refs/heads/master/macos.keybindings.json?t=#{Time.now.to_i}"))) | |
| windows = JSON.parse(Net::HTTP.get(URI("https://raw.githubusercontent.com/codebling/vs-code-default-keybindings/refs/heads/master/windows.keybindings.json?t=#{Time.now.to_i}"))) | |
| # [[command: str, when: str, args: object], key: Array<Set<string>>] | |
| macos = macos.to_h do |e| | |
| key = e['key'].split.map { |s| Set.new(s.split('+')) } | |
| [[e['command'], e['when'], e['args']], key] | |
| end | |
| windows = windows.to_h do |e| | |
| key = e['key'].split.map { |e| Set.new(e.split('+')) } | |
| [[e['command'], e['when'], e['args']], key] | |
| end | |
| #### MacOS にのみあるやつ | |
| puts '--- Processing MacOS only ---' | |
| add_entries = [] | |
| # macosonly = macos.reject { |k, _v| windows.key?(k) } | |
| # macosonly.each do |(command, when_str, args), key_seq| | |
| # entry = { command: command, when: when_str, args: args } | |
| # strokes = key_seq.map { |stroke| replace(stroke) } | |
| # if strokes.all? | |
| # entry[:key] = strokes.map { |s| s.join('+') }.join(' ') | |
| # add_entries << entry.compact | |
| # next | |
| # end | |
| # puts "Cannot map: #{command} @#{when_str}" | |
| # puts " keys: #{key_seq.map { |s| s.join('+') }.join(' ')}" | |
| # end | |
| puts "--- End MacOS only (added #{add_entries.size}) ---" | |
| #### Windows にのみあるやつ(いらない) | |
| puts '--- Windows only (removing) ---' | |
| remove_entries = [] | |
| windowsonly = windows.reject { |k, _v| macos.key?(k) } | |
| windowsonly.each do |(command, when_str, args), keys| | |
| # workbench.action.terminal. 以外は残しとく | |
| next unless command.start_with?('workbench.action.terminal.') | |
| entry = { command: command, when: when_str, args: args, keys: keys.map { |s| s.join('+') }.join(' ') } | |
| remove_entries << entry.compact | |
| end | |
| puts "--- End Windows only (removed #{remove_entries.size}) ---" | |
| #### 両方にあるやつ(置き換え) | |
| puts '--- Processing Both OS ---' | |
| replace_entries = [] | |
| both = windows.select { |k, _v| macos.key?(k) } | |
| both.each do |(command, when_str, args), win_keys| | |
| mac_keys = macos[[command, when_str, args]] | |
| replaced_mac_keys = mac_keys.map { |stroke| replace(stroke) } | |
| next if win_keys == replaced_mac_keys # 同じならスキップ | |
| # mac のほうに ctrl がある | |
| unless replaced_mac_keys.all? | |
| next if mac_keys == win_keys # そういうこともある | |
| puts "Cannot map: #{command} @#{when_str}" | |
| puts " mac keys: #{mac_keys.map { |s| s.join('+') }.join(' ')}" | |
| puts " win keys: #{win_keys.map { |s| s.join('+') }.join(' ')}" | |
| next | |
| end | |
| replace_entries << { command: command, when: when_str, args: args, | |
| win_key: win_keys.map { |s| s.join('+') }.join(' '), | |
| key: replaced_mac_keys.map { |s| s.join('+') }.join(' ') } | |
| end | |
| puts "--- End Both OS (replaced #{replace_entries.size}) ---" | |
| #### 手動追加 | |
| # ターミナル系 | |
| @ascii_arrays.each_with_index do |_k, i| | |
| a = %w[ctrl alt shift][i / 12] | |
| b = "f#{13 + (i % 12)}" | |
| add_entries << { | |
| command: 'workbench.action.terminal.sendSequence', | |
| when: 'terminalFocus', | |
| args: { text: i.chr }, key: "#{a}+#{b}" | |
| } | |
| end | |
| add_entries << { command: 'workbench.action.terminal.sendSequence', | |
| when: "terminalFocus && terminalShellType == 'cmd'", | |
| args: { text: "\b" }, keys: 'shift+f24' } # = Ctrl-BS | |
| add_entries << { command: 'workbench.action.terminal.sendSequence', | |
| when: 'terminalFocus', | |
| args: { text: "\1bb" }, key: 'ctrl+left' } | |
| add_entries << { command: 'workbench.action.terminal.sendSequence', | |
| when: 'terminalFocus', | |
| args: { text: "\u001bf" }, key: 'ctrl+right' } | |
| # ターミナルでのコピペ | |
| add_entries << { command: 'workbench.action.terminal.paste', | |
| when: 'terminalFocus', key: 'ctrl+v' } | |
| add_entries << { command: 'workbench.action.terminal.copySelection', | |
| when: 'terminalFocus', key: 'ctrl+c' } | |
| remove_entries << { command: 'workbench.action.terminal.paste', | |
| when: 'terminalFocus && terminalHasBeenCreated || terminalFocus && terminalProcessSupported', | |
| key: 'ctrl+v' } | |
| # テキスト操作系 | |
| add_entries << { key: 'ctrl+f14', | |
| command: 'cursorHome', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+shift+f14', | |
| command: 'cursorHomeSelect', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+f15', | |
| command: 'cursorLeft', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+shift+f15', | |
| command: 'cursorLeftSelect', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+f17', | |
| command: 'deleteRight', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+f18', | |
| command: 'cursorEnd', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+shift+f18', | |
| command: 'cursorEndSelect', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+f19', | |
| command: 'cursorRight', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+shift+f19', | |
| command: 'cursorRightSelect', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+f21', | |
| command: 'deleteLeft', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'alt+f15', | |
| command: 'cursorDown', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'alt+shift+f15', | |
| command: 'cursorDownSelect', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'alt+f17', | |
| command: 'cursorUp', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'alt+shift+f17', | |
| command: 'cursorUpSelect', | |
| when: 'textInputFocus' } | |
| # 雑多なもの | |
| add_entries << { key: 'ctrl+home', | |
| command: 'cursorTop', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+end', | |
| command: 'cursorBottom', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+shift+home', | |
| command: 'cursorTopSelect', | |
| when: 'textInputFocus' } | |
| add_entries << { key: 'ctrl+shift+end', | |
| command: 'cursorBottomSelect', | |
| when: 'textInputFocus' } | |
| #### 出力 | |
| out = [] | |
| # add | |
| out += add_entries.sort_by { |e| e['command'] } | |
| # remove | |
| rem = remove_entries.sort_by { |e| e['command'] } | |
| out += rem.map { |e| { command: "-#{e[:command]}", when: e[:when], args: e[:args], key: e[:key] } } | |
| # replace | |
| rep = replace_entries.sort_by { |e| e['command'] } | |
| rep.each do |e| | |
| out << { command: "-#{e[:command]}", when: e[:when], args: e[:args], key: e[:win_key] } | |
| out << { command: e[:command], when: e[:when], args: e[:args], key: e[:key] } | |
| end | |
| File.write('keybindings.json', JSON.pretty_generate(out.map(&:compact))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment