diff --git a/.gitignore b/.gitignore index 11d0b51..b67bdf6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ build-*/ dist/ +.idea \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ac38046..9b21666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,13 +9,20 @@ configure_file( add_library(VSCodeKeymapNpp SHARED src/VSCodeKeymapPlugin.cpp + src/handlers/ActionHandlers.cpp + src/handlers/CommentHandlers.cpp + src/handlers/CursorHandlers.cpp + src/handlers/FoldHandlers.cpp + src/handlers/LineHandlers.cpp + src/handlers/ScrollHandlers.cpp src/GeneratedBindings.inc ${CMAKE_CURRENT_BINARY_DIR}/VSCodeKeymapNpp.rc ) -target_include_directories(VSCodeKeymapNpp PRIVATE include) +target_include_directories(VSCodeKeymapNpp PRIVATE include src) target_compile_features(VSCodeKeymapNpp PRIVATE cxx_std_20) target_compile_definitions(VSCodeKeymapNpp PRIVATE UNICODE _UNICODE WIN32_LEAN_AND_MEAN NOMINMAX) +target_link_libraries(VSCodeKeymapNpp PRIVATE comctl32) set_target_properties(VSCodeKeymapNpp PROPERTIES OUTPUT_NAME "VSCodeKeymapNpp" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9806080..de592db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,9 +86,8 @@ notepad++.exe -noPlugin -multiInst -nosession -settingsDir= These files are generated and should stay in sync: -1. `data/vscode-default-keybindings.windows.json` -2. `docs/VSCodeKeybindingCoverage.MD` -3. `src/GeneratedBindings.inc` +1. `docs/VSCodeKeybindingCoverage.MD` +2. `src/GeneratedBindings.inc` If you change `data/keybinding-mappings.json` or the generator script, regenerate those files in the same change. diff --git a/README.MD b/README.MD index 0b7a617..d025947 100644 --- a/README.MD +++ b/README.MD @@ -14,10 +14,10 @@ documented as intentionally unported. - Runtime bindings come from generated tables in `src/GeneratedBindings.inc` - Release flow now supports `x86`, `x64`, and `arm64` -The plugin still registers two menu commands: +The plugin currently registers two menu commands: -- `Plugins > VSCode Keymap NPP > Toggle VSCode Strict Keymap` -- `Plugins > VSCode Keymap NPP > Show VSCode Coverage Summary` +- `Plugins > VSCode Keymap NPP > Config/Reference` +- `Plugins > VSCode Keymap NPP > About` ## Coverage model @@ -31,16 +31,15 @@ Bindings land in one of three states: - `documented-unported`: listed, but intentionally not emulated Current generated totals live in the coverage report and are also shown in the -plugin summary dialog. +plugin About dialog. ## Repository layout - `src/VSCodeKeymapPlugin.cpp`: plugin runtime and custom editor actions - `src/GeneratedBindings.inc`: generated runtime binding tables - `data/keybinding-mappings.json`: mapping manifest for portable shortcuts -- `data/vscode-default-keybindings.windows.json`: generated normalized source snapshot - `docs/VSCodeKeybindingCoverage.MD`: generated coverage report -- `scripts/sync-vscode-keybindings.ps1`: refresh source snapshot, report, and runtime tables +- `scripts/sync-vscode-keybindings.ps1`: refresh the source scrape, report, and embedded runtime/reference tables - `scripts/package-release.ps1`: build and package release zips ## Refresh generated shortcut data @@ -55,9 +54,8 @@ powershell -ExecutionPolicy Bypass -File .\scripts\sync-vscode-keybindings.ps1 That script will: - fetch the current VS Code default keybindings page -- regenerate the normalized Windows shortcut snapshot - validate manifest coverage and runtime collisions -- regenerate the runtime include file +- regenerate the embedded runtime/reference include file - regenerate the Markdown coverage report ## Build @@ -117,8 +115,9 @@ Artifacts: Each zip is Plugin Admin-friendly: - `VSCodeKeymapNpp.dll` at archive root -- `doc/README.MD` -- `doc/VSCodeKeybindingCoverage.MD` + +The DLL carries the reference data used by the config dialog, so deployment no +longer requires shipping the generated docs beside it. ## Install @@ -138,6 +137,10 @@ Or under a machine-wide Notepad++ install: Use the DLL whose architecture matches the installed Notepad++ build exactly. Restart Notepad++ after copying the plugin. +Inside `Plugins > VSCode Keymap NPP > Config/Reference`, checked rows are the +shortcuts currently handled by the plugin. Clearing a row passes that shortcut +back through to Notepad++. + ## Feedback and contributions If a shortcut still behaves differently from VS Code, opens the wrong diff --git a/data/keybinding-mappings.json b/data/keybinding-mappings.json index 6c671fe..bcbea2b 100644 --- a/data/keybinding-mappings.json +++ b/data/keybinding-mappings.json @@ -58,16 +58,16 @@ { "command": "editor.action.copyLinesDownAction", "winKey": "Shift+Alt+Down", - "handler": "nppCommand", - "target": "IDM_EDIT_DUP_LINE", - "notes": "Duplicate line downward." + "handler": "custom", + "target": "DuplicateLineDown", + "notes": "Duplicate line downward and place the caret in the duplicate." }, { "command": "editor.action.copyLinesUpAction", "winKey": "Shift+Alt+Up", "handler": "custom", "target": "DuplicateLineUp", - "notes": "Duplicate line, then move duplicate upward." + "notes": "Duplicate line upward and place the caret in the duplicate." }, { "command": "undo", @@ -132,14 +132,34 @@ { "command": "editor.action.insertCursorBelow", "winKey": "Ctrl+Alt+Down", - "handler": "reservedNoOp", - "notes": "Reserved so Notepad++ defaults do not hijack VS Code multi-cursor shortcut." + "handler": "custom", + "target": "InsertCursorBelow", + "notes": "Move with the shared vertical-cursor rules; add/select one caret below. Virtual-space alignment is configurable." }, { "command": "editor.action.insertCursorAbove", "winKey": "Ctrl+Alt+Up", - "handler": "reservedNoOp", - "notes": "Reserved so Notepad++ defaults do not hijack VS Code multi-cursor shortcut." + "handler": "custom", + "target": "InsertCursorAbove", + "notes": "Move with the shared vertical-cursor rules; add/select one caret above." + }, + { + "command": "editor.action.cursorColumnSelectDown", + "winKey": "Ctrl+Alt+Shift+Down", + "section": "Basic Editing", + "label": "Column Select Down", + "handler": "custom", + "target": "CursorColumnSelectDown", + "notes": "Move with the shared vertical-cursor rules; rebuild the anchored column-selection range downward. Undocumented on the VS Code default keybindings page, but available in the VS Code Keyboard Shortcuts editor." + }, + { + "command": "editor.action.cursorColumnSelectUp", + "winKey": "Ctrl+Alt+Shift+Up", + "section": "Basic Editing", + "label": "Column Select Up", + "handler": "custom", + "target": "CursorColumnSelectUp", + "notes": "Move with the shared vertical-cursor rules; rebuild the anchored column-selection range upward. Undocumented on the VS Code default keybindings page, but available in the VS Code Keyboard Shortcuts editor." }, { "command": "cursorTop", diff --git a/data/vscode-default-keybindings.windows.json b/data/vscode-default-keybindings.windows.json deleted file mode 100644 index 611cd03..0000000 --- a/data/vscode-default-keybindings.windows.json +++ /dev/null @@ -1,1946 +0,0 @@ -{ - "source": { - "url": "https://code.visualstudio.com/docs/reference/default-keybindings", - "msDate": "4/22/2026" - }, - "counts": { - "unported": 40, - "reserved": 39, - "total": 161, - "mapped": 82 - }, - "bindings": [ - { - "section": "Basic Editing", - "subsection": "", - "label": "Cut line (empty selection)", - "command": "editor.action.clipboardCutAction", - "winKey": "Ctrl+X", - "status": "mapped", - "handler": "custom", - "target": "CutAllowLine", - "notes": "Cut whole line when selection is empty, like VS Code.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Copy line (empty selection)", - "command": "editor.action.clipboardCopyAction", - "winKey": "Ctrl+C", - "status": "mapped", - "handler": "custom", - "target": "CopyAllowLine", - "notes": "Copy whole line when selection is empty, like VS Code.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Paste", - "command": "editor.action.clipboardPasteAction", - "winKey": "Ctrl+V", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for paste.", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Delete Line", - "command": "editor.action.deleteLines", - "winKey": "Ctrl+Shift+K", - "status": "mapped", - "handler": "sciCommand", - "target": "SCI_LINEDELETE", - "notes": "Delete current line when selection is empty, matching VS Code behavior closely.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Insert Line Below", - "command": "editor.action.insertLineAfter", - "winKey": "Ctrl+Enter", - "status": "mapped", - "handler": "custom", - "target": "InsertLineBelow", - "notes": "Insert blank line below caret.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Insert Line Above", - "command": "editor.action.insertLineBefore", - "winKey": "Ctrl+Shift+Enter", - "status": "mapped", - "handler": "custom", - "target": "InsertLineAbove", - "notes": "Insert blank line above caret.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Move Line Down", - "command": "editor.action.moveLinesDownAction", - "winKey": "Alt+Down", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_LINE_DOWN", - "notes": "Move current line or selection down.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Move Line Up", - "command": "editor.action.moveLinesUpAction", - "winKey": "Alt+Up", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_LINE_UP", - "notes": "Move current line or selection up.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Copy Line Down", - "command": "editor.action.copyLinesDownAction", - "winKey": "Shift+Alt+Down", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_DUP_LINE", - "notes": "Duplicate line downward.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Copy Line Up", - "command": "editor.action.copyLinesUpAction", - "winKey": "Shift+Alt+Up", - "status": "mapped", - "handler": "custom", - "target": "DuplicateLineUp", - "notes": "Duplicate line, then move duplicate upward.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Undo", - "command": "undo", - "winKey": "Ctrl+Z", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for undo.", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Redo", - "command": "redo", - "winKey": "Ctrl+Y", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for redo.", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Add Selection To Next Find Match", - "command": "editor.action.addSelectionToNextFindMatch", - "winKey": "Ctrl+D", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_MULTISELECTNEXT", - "notes": "Add next match to multiselection.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Move Last Selection To Next Find Match", - "command": "editor.action.moveSelectionToNextFindMatch", - "winKey": "Ctrl+K Ctrl+D", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_MULTISELECTSSKIP", - "notes": "Skip current occurrence in multi-select flow.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Undo last cursor operation", - "command": "cursorUndo", - "winKey": "Ctrl+U", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_MULTISELECTUNDO", - "notes": "Undo last multi-cursor selection step.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Insert cursor at end of each line selected", - "command": "editor.action.insertCursorAtEndOfEachLineSelected", - "winKey": "Shift+Alt+I", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Multiple-caret insertion per line is not implemented safely in Notepad++.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Select all occurrences of current selection", - "command": "editor.action.selectHighlights", - "winKey": "Ctrl+Shift+L", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_MULTISELECTALL", - "notes": "Select all current selection matches.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Select all occurrences of current word", - "command": "editor.action.changeAll", - "winKey": "Ctrl+F2", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_MULTISELECTALLWHOLEWORD", - "notes": "Approximate current-word multi-select.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Select current line", - "command": "expandLineSelection", - "winKey": "Ctrl+L", - "status": "mapped", - "handler": "custom", - "target": "SelectCurrentLine", - "notes": "Select current line without trailing newline.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Insert Cursor Below", - "command": "editor.action.insertCursorBelow", - "winKey": "Ctrl+Alt+Down", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so Notepad++ defaults do not hijack VS Code multi-cursor shortcut.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Insert Cursor Above", - "command": "editor.action.insertCursorAbove", - "winKey": "Ctrl+Alt+Up", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so Notepad++ defaults do not hijack VS Code multi-cursor shortcut.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Jump to matching bracket", - "command": "editor.action.jumpToBracket", - "winKey": "Ctrl+Shift+\\", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_SEARCH_GOTOMATCHINGBRACE", - "notes": "Jump to matching brace.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Indent Line", - "command": "editor.action.indentLines", - "winKey": "Ctrl+]", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_INS_TAB", - "notes": "Approximate VS Code indent with Notepad++ indent command.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Outdent Line", - "command": "editor.action.outdentLines", - "winKey": "Ctrl+[", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_RMV_TAB", - "notes": "Approximate VS Code outdent with Notepad++ unindent command.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Go to Beginning of Line", - "command": "cursorHome", - "winKey": "Home", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for line-home movement.", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Go to End of Line", - "command": "cursorEnd", - "winKey": "End", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for line-end movement.", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Go to End of File", - "command": "cursorBottom", - "winKey": "Ctrl+End", - "status": "mapped", - "handler": "sciCommand", - "target": "SCI_DOCUMENTEND", - "notes": "Jump to document end.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Go to Beginning of File", - "command": "cursorTop", - "winKey": "Ctrl+Home", - "status": "mapped", - "handler": "sciCommand", - "target": "SCI_DOCUMENTSTART", - "notes": "Jump to document start.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Scroll Line Down", - "command": "scrollLineDown", - "winKey": "Ctrl+Down", - "status": "mapped", - "handler": "sciCommand", - "target": "SCI_LINESCROLLDOWN", - "notes": "Scroll without moving caret.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Scroll Line Up", - "command": "scrollLineUp", - "winKey": "Ctrl+Up", - "status": "mapped", - "handler": "sciCommand", - "target": "SCI_LINESCROLLUP", - "notes": "Scroll without moving caret.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Scroll Page Down", - "command": "scrollPageDown", - "winKey": "Alt+PageDown", - "status": "mapped", - "handler": "custom", - "target": "ScrollPageDownNoCaret", - "notes": "Scroll a page without moving caret.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Scroll Page Up", - "command": "scrollPageUp", - "winKey": "Alt+PageUp", - "status": "mapped", - "handler": "custom", - "target": "ScrollPageUpNoCaret", - "notes": "Scroll a page without moving caret.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Fold (collapse) region", - "command": "editor.fold", - "winKey": "Ctrl+Shift+[", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_FOLD_CURRENT", - "notes": "Fold current region.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Unfold (uncollapse) region", - "command": "editor.unfold", - "winKey": "Ctrl+Shift+]", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_UNFOLD_CURRENT", - "notes": "Unfold current region.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Toggle Fold region", - "command": "editor.toggleFold", - "winKey": "Ctrl+K Ctrl+L", - "status": "mapped", - "handler": "custom", - "target": "ToggleFoldAtCaret", - "notes": "Toggle fold header at caret, or nearest fold parent.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Fold (collapse) all subregions", - "command": "editor.foldRecursively", - "winKey": "Ctrl+K Ctrl+[", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved chord; exact recursive fold not implemented.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Unfold (uncollapse) all subregions", - "command": "editor.unfoldRecursively", - "winKey": "Ctrl+K Ctrl+]", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved chord; exact recursive unfold not implemented.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Fold (collapse) all regions", - "command": "editor.foldAll", - "winKey": "Ctrl+K Ctrl+0", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_FOLDALL", - "notes": "Fold all regions.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Unfold (uncollapse) all regions", - "command": "editor.unfoldAll", - "winKey": "Ctrl+K Ctrl+J", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_UNFOLDALL", - "notes": "Unfold all regions.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Add Line Comment", - "command": "editor.action.addCommentLine", - "winKey": "Ctrl+K Ctrl+C", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_STREAM_COMMENT", - "notes": "Force comment selected lines.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Remove Line Comment", - "command": "editor.action.removeCommentLine", - "winKey": "Ctrl+K Ctrl+U", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_STREAM_UNCOMMENT", - "notes": "Force uncomment selected lines.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Toggle Line Comment", - "command": "editor.action.commentLine", - "winKey": "Ctrl+/", - "status": "mapped", - "handler": "custom", - "target": "ToggleStreamComment", - "notes": "Toggle line-style stream comment based on current selection state.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Toggle Block Comment", - "command": "editor.action.blockComment", - "winKey": "Shift+Alt+A", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_BLOCK_COMMENT", - "notes": "Use Notepad++ block comment command.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Find", - "command": "actions.find", - "winKey": "Ctrl+F", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for find.", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Replace", - "command": "editor.action.startFindReplaceAction", - "winKey": "Ctrl+H", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for replace.", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Find Next", - "command": "editor.action.nextMatchFindAction", - "winKey": "Enter", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Find Previous", - "command": "editor.action.previousMatchFindAction", - "winKey": "Shift+Enter", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Select All Occurrences of Find Match", - "command": "editor.action.selectAllMatches", - "winKey": "Alt+Enter", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_MULTISELECTALL", - "notes": "Select all find matches.", - "runtime": true - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Toggle Find Case Sensitive", - "command": "toggleFindCaseSensitive", - "winKey": "Alt+C", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Toggle Find Regex", - "command": "toggleFindRegex", - "winKey": "Alt+R", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Toggle Find Whole Word", - "command": "toggleFindWholeWord", - "winKey": "Alt+W", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Toggle Use of Tab Key for Setting Focus", - "command": "editor.action.toggleTabFocusMode", - "winKey": "Ctrl+M", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Basic Editing", - "subsection": "", - "label": "Toggle Word Wrap", - "command": "editor.action.toggleWordWrap", - "winKey": "Alt+Z", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_WRAP", - "notes": "Toggle word wrap.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Trigger Suggest", - "command": "editor.action.triggerSuggest", - "winKey": "Ctrl+Space", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_AUTOCOMPLETE", - "notes": "Open autocomplete.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Trigger Parameter Hints", - "command": "editor.action.triggerParameterHints", - "winKey": "Ctrl+Shift+Space", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_FUNCCALLTIP", - "notes": "Open function call tip.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Format Document", - "command": "editor.action.formatDocument", - "winKey": "Shift+Alt+F", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Formatting depends on plugins/language support, so strict mode reserves it.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Format Selection", - "command": "editor.action.formatSelection", - "winKey": "Ctrl+K Ctrl+F", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code format-selection chord.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Go to Definition", - "command": "editor.action.revealDefinition", - "winKey": "F12", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No built-in go-to-definition equivalent.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Show Hover", - "command": "editor.action.showHover", - "winKey": "Ctrl+K Ctrl+I", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code hover chord.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Peek Definition", - "command": "editor.action.peekDefinition", - "winKey": "Alt+F12", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No peek-definition equivalent.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Open Definition to the Side", - "command": "editor.action.revealDefinitionAside", - "winKey": "Ctrl+K F12", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No open-definition-side equivalent.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Quick Fix", - "command": "editor.action.quickFix", - "winKey": "Ctrl+.", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No quick-fix provider equivalent.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Go to References", - "command": "editor.action.goToReferences", - "winKey": "Shift+F12", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No find-references equivalent.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Rename Symbol", - "command": "editor.action.rename", - "winKey": "F2", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No language-aware rename equivalent.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Replace with Next Value", - "command": "editor.action.inPlaceReplace.down", - "winKey": "Ctrl+Shift+.", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Replace with Previous Value", - "command": "editor.action.inPlaceReplace.up", - "winKey": "Ctrl+Shift+,", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Expand AST Selection", - "command": "editor.action.smartSelect.expand", - "winKey": "Shift+Alt+Right", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Shrink AST Selection", - "command": "editor.action.smartSelect.shrink", - "winKey": "Shift+Alt+Left", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Trim Trailing Whitespace", - "command": "editor.action.trimTrailingWhitespace", - "winKey": "Ctrl+K Ctrl+X", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_TRIMTRAILING", - "notes": "Trim trailing whitespace.", - "runtime": true - }, - { - "section": "Rich Languages Editing", - "subsection": "", - "label": "Change Language Mode", - "command": "workbench.action.editor.changeLanguageMode", - "winKey": "Ctrl+K M", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code language-mode chord.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Show All Symbols", - "command": "workbench.action.showAllSymbols", - "winKey": "Ctrl+T", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_SWITCHTO_FUNC_LIST", - "notes": "Approximate workspace symbol search with function list.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Go to Line...", - "command": "workbench.action.gotoLine", - "winKey": "Ctrl+G", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_SEARCH_GOTOLINE", - "notes": "Go to line.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Go to File..., Quick Open", - "command": "workbench.action.quickOpen", - "winKey": "Ctrl+P", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_FILE_OPEN", - "notes": "Approximate Quick Open with file-open dialog.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Go to Symbol...", - "command": "workbench.action.gotoSymbol", - "winKey": "Ctrl+Shift+O", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_SWITCHTO_FUNC_LIST", - "notes": "Approximate symbol picker with function list panel.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Show Problems", - "command": "workbench.actions.view.problems", - "winKey": "Ctrl+Shift+M", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code problems shortcut.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Go to Next Error or Warning", - "command": "editor.action.marker.nextInFiles", - "winKey": "F8", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No Problems panel equivalent.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Go to Previous Error or Warning", - "command": "editor.action.marker.prevInFiles", - "winKey": "Shift+F8", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No Problems panel equivalent.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Show All Commands", - "command": "workbench.action.showCommands", - "winKey": "Ctrl+Shift+P", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No Command Palette equivalent.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Navigate Editor Group History", - "command": "workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup", - "winKey": "Ctrl+Tab", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_TAB_NEXT", - "notes": "Cycle tabs forward.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Go Back", - "command": "workbench.action.navigateBack", - "winKey": "Alt+Left", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No stable back-stack equivalent.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Go back in Quick Input", - "command": "workbench.action.quickInputBack", - "winKey": "Alt+Left", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Navigation", - "subsection": "", - "label": "Go Forward", - "command": "workbench.action.navigateForward", - "winKey": "Alt+Right", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No stable forward-stack equivalent.", - "runtime": true - }, - { - "section": "Navigation", - "subsection": "", - "label": "Focus Breadcrumbs", - "command": "breadcrumbs.focus", - "winKey": "Ctrl+Shift+;", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Navigation", - "subsection": "", - "label": "Focus and Select Breadcrumbs", - "command": "breadcrumbs.focusAndSelect", - "winKey": "Ctrl+Shift+.", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "New Window", - "command": "workbench.action.newWindow", - "winKey": "Ctrl+Shift+N", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_GOTO_NEW_INSTANCE", - "notes": "Approximate new window with new Notepad++ instance.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Close Window", - "command": "workbench.action.closeWindow", - "winKey": "Alt+F4", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Close Editor", - "command": "workbench.action.closeActiveEditor", - "winKey": "Ctrl+F4", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_FILE_CLOSE", - "notes": "Close active file.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Close Folder", - "command": "workbench.action.closeFolder", - "winKey": "Ctrl+K F", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code close-folder chord.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Split Editor", - "command": "workbench.action.splitEditor", - "winKey": "Ctrl+\\", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_GOTO_ANOTHER_VIEW", - "notes": "Approximate split editor by moving document to the other view.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Focus into First Editor Group", - "command": "workbench.action.focusFirstEditorGroup", - "winKey": "Ctrl+1", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_TAB1", - "notes": "Approximate first editor group focus with first tab selection.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Focus into Second Editor Group", - "command": "workbench.action.focusSecondEditorGroup", - "winKey": "Ctrl+2", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_TAB2", - "notes": "Approximate second editor group focus with second tab selection.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Focus into Third Editor Group", - "command": "workbench.action.focusThirdEditorGroup", - "winKey": "Ctrl+3", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_TAB3", - "notes": "Approximate third editor group focus with third tab selection.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Focus into Editor Group on the Left", - "command": "workbench.action.focusLeftGroup", - "winKey": "Ctrl+K Ctrl+Left", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code focus-group chord.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Focus into Editor Group on the Right", - "command": "workbench.action.focusRightGroup", - "winKey": "Ctrl+K Ctrl+Right", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code focus-group chord.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Move Editor Left", - "command": "workbench.action.moveEditorLeftInGroup", - "winKey": "Ctrl+Shift+PageUp", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_TAB_MOVEBACKWARD", - "notes": "Approximate move editor left with move tab left.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Move Editor Right", - "command": "workbench.action.moveEditorRightInGroup", - "winKey": "Ctrl+Shift+PageDown", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_TAB_MOVEFORWARD", - "notes": "Approximate move editor right with move tab right.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Move Active Editor Group Left", - "command": "workbench.action.moveActiveEditorGroupLeft", - "winKey": "Ctrl+K Left", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code move-group chord.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Move Active Editor Group Right", - "command": "workbench.action.moveActiveEditorGroupRight", - "winKey": "Ctrl+K Right", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code move-group chord.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Move Editor into Next Group", - "command": "workbench.action.moveEditorToNextGroup", - "winKey": "Ctrl+Alt+Right", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_GOTO_ANOTHER_VIEW", - "notes": "Approximate move editor to next group by moving document to the other view.", - "runtime": true - }, - { - "section": "Editor/Window Management", - "subsection": "", - "label": "Move Editor into Previous Group", - "command": "workbench.action.moveEditorToPreviousGroup", - "winKey": "Ctrl+Alt+Left", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_GOTO_ANOTHER_VIEW", - "notes": "Approximate move editor to previous group by moving document to the other view.", - "runtime": true - }, - { - "section": "File Management", - "subsection": "", - "label": "New File", - "command": "workbench.action.files.newUntitledFile", - "winKey": "Ctrl+N", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for new file.", - "runtime": false - }, - { - "section": "File Management", - "subsection": "", - "label": "Open File...", - "command": "workbench.action.files.openFile", - "winKey": "Ctrl+O", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for open file.", - "runtime": false - }, - { - "section": "File Management", - "subsection": "", - "label": "Save", - "command": "workbench.action.files.save", - "winKey": "Ctrl+S", - "status": "mapped", - "handler": "nativePassThrough", - "target": null, - "notes": "Notepad++ already matches VS Code for save.", - "runtime": false - }, - { - "section": "File Management", - "subsection": "", - "label": "Save All", - "command": "saveAll", - "winKey": "Ctrl+K S", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_FILE_SAVEALL", - "notes": "Save all open files.", - "runtime": true - }, - { - "section": "File Management", - "subsection": "", - "label": "Save As...", - "command": "workbench.action.files.saveAs", - "winKey": "Ctrl+Shift+S", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_FILE_SAVEAS", - "notes": "Save current file as.", - "runtime": true - }, - { - "section": "File Management", - "subsection": "", - "label": "Close Group", - "command": "workbench.action.closeEditorsInGroup", - "winKey": "Ctrl+K W", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code close-group chord.", - "runtime": true - }, - { - "section": "File Management", - "subsection": "", - "label": "Close All", - "command": "workbench.action.closeAllEditors", - "winKey": "Ctrl+K Ctrl+W", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_FILE_CLOSEALL", - "notes": "Close all files.", - "runtime": true - }, - { - "section": "File Management", - "subsection": "", - "label": "Reopen Closed Editor", - "command": "workbench.action.reopenClosedEditor", - "winKey": "Ctrl+Shift+T", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_FILE_RESTORELASTCLOSEDFILE", - "notes": "Restore last closed file.", - "runtime": true - }, - { - "section": "File Management", - "subsection": "", - "label": "Keep Open", - "command": "workbench.action.keepEditor", - "winKey": "Ctrl+K Enter", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code keep-open chord.", - "runtime": true - }, - { - "section": "File Management", - "subsection": "", - "label": "Copy Path of Active File", - "command": "workbench.action.files.copyPathOfActiveFile", - "winKey": "Ctrl+K P", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_FULLPATHTOCLIP", - "notes": "Copy active file path.", - "runtime": true - }, - { - "section": "File Management", - "subsection": "", - "label": "Reveal Active File in Windows", - "command": "workbench.action.files.revealActiveFileInWindows", - "winKey": "Ctrl+K R", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_EDIT_OPENINFOLDER", - "notes": "Reveal active file in Explorer.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Toggle Full Screen", - "command": "workbench.action.toggleFullScreen", - "winKey": "F11", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_FULLSCREENTOGGLE", - "notes": "Toggle fullscreen.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Toggle Zen Mode", - "command": "workbench.action.toggleZenMode", - "winKey": "Ctrl+K Z", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_DISTRACTIONFREE", - "notes": "Approximate VS Code Zen Mode with Notepad++ distraction-free mode.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Leave Zen Mode", - "command": "workbench.action.exitZenMode", - "winKey": "Escape Escape", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Display", - "subsection": "", - "label": "Zoom in", - "command": "workbench.action.zoomIn", - "winKey": "Ctrl+=", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_ZOOMIN", - "notes": "Zoom in.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Zoom out", - "command": "workbench.action.zoomOut", - "winKey": "Ctrl+-", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_ZOOMOUT", - "notes": "Zoom out.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Reset Zoom", - "command": "workbench.action.zoomReset", - "winKey": "Ctrl+Numpad0", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_ZOOMRESTORE", - "notes": "Reset zoom.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Toggle Sidebar Visibility", - "command": "workbench.action.toggleSidebarVisibility", - "winKey": "Ctrl+B", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_FILEBROWSER", - "notes": "Approximate sidebar toggle with file browser panel.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Show Explorer / Toggle Focus", - "command": "workbench.view.explorer", - "winKey": "Ctrl+Shift+E", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_VIEW_SWITCHTO_FILEBROWSER", - "notes": "Focus file browser panel.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Show Search", - "command": "workbench.view.search", - "winKey": "Ctrl+Shift+F", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_SEARCH_FINDINFILES", - "notes": "Find in files.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Show Source Control", - "command": "workbench.view.scm", - "winKey": "Ctrl+Shift+G", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No source-control sidebar equivalent.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Show Run", - "command": "workbench.view.debug", - "winKey": "Ctrl+Shift+D", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No Run/Debug sidebar equivalent.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Show Extensions", - "command": "workbench.view.extensions", - "winKey": "Ctrl+Shift+X", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No extensions marketplace sidebar equivalent.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Show Output", - "command": "workbench.action.output.toggleOutput", - "winKey": "Ctrl+Shift+U", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "No Output panel equivalent.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Quick Open View", - "command": "workbench.action.quickOpenView", - "winKey": "Ctrl+Q", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code quick-open-view shortcut.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Open New Command Prompt", - "command": "workbench.action.terminal.openNativeConsole", - "winKey": "Ctrl+Shift+C", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_FILE_OPEN_CMD", - "notes": "Approximate with Open Command Prompt Here.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Toggle Markdown Preview", - "command": "markdown.showPreview", - "winKey": "Ctrl+Shift+V", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code markdown-preview shortcut.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Open Preview to the Side", - "command": "markdown.showPreviewToSide", - "winKey": "Ctrl+K V", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code markdown-preview chord.", - "runtime": true - }, - { - "section": "Display", - "subsection": "", - "label": "Toggle Integrated Terminal", - "command": "workbench.action.terminal.toggleTerminal", - "winKey": "Ctrl+`", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_FILE_OPEN_CMD", - "notes": "Approximate integrated terminal with Open Command Prompt Here.", - "runtime": true - }, - { - "section": "Search", - "subsection": "", - "label": "Replace in Files", - "command": "workbench.action.replaceInFiles", - "winKey": "Ctrl+Shift+H", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_SEARCH_FINDINFILES", - "notes": "Approximate replace-in-files with Notepad++ find-in-files panel.", - "runtime": true - }, - { - "section": "Search", - "subsection": "", - "label": "Toggle Match Case", - "command": "toggleSearchCaseSensitive", - "winKey": "Alt+C", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search", - "subsection": "", - "label": "Toggle Match Whole Word", - "command": "toggleSearchWholeWord", - "winKey": "Alt+W", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search", - "subsection": "", - "label": "Toggle Use Regular Expression", - "command": "toggleSearchRegex", - "winKey": "Alt+R", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search", - "subsection": "", - "label": "Toggle Search Details", - "command": "workbench.action.search.toggleQueryDetails", - "winKey": "Ctrl+Shift+J", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code search-details shortcut.", - "runtime": true - }, - { - "section": "Search", - "subsection": "", - "label": "Focus Next Search Result", - "command": "search.action.focusNextSearchResult", - "winKey": "F4", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search", - "subsection": "", - "label": "Focus Previous Search Result", - "command": "search.action.focusPreviousSearchResult", - "winKey": "Shift+F4", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search", - "subsection": "", - "label": "Show Next Search Term", - "command": "history.showNext", - "winKey": "Down", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search", - "subsection": "", - "label": "Show Previous Search Term", - "command": "history.showPrevious", - "winKey": "Up", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search Editor", - "subsection": "", - "label": "Open Results In Editor", - "command": "search.action.openInEditor", - "winKey": "Alt+Enter", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search Editor", - "subsection": "", - "label": "Focus Search Editor Input", - "command": "search.action.focusQueryEditorWidget", - "winKey": "Escape", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search Editor", - "subsection": "", - "label": "Search Again", - "command": "rerunSearchEditorSearch", - "winKey": "Ctrl+Shift+R", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Search Editor", - "subsection": "", - "label": "Delete File Results", - "command": "search.searchEditor.action.deleteFileResults", - "winKey": "Ctrl+Shift+Backspace", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Preferences", - "subsection": "", - "label": "Open Settings", - "command": "workbench.action.openSettings", - "winKey": "Ctrl+,", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_SETTING_PREFERENCE", - "notes": "Open Notepad++ preferences.", - "runtime": true - }, - { - "section": "Preferences", - "subsection": "", - "label": "Open Keyboard Shortcuts", - "command": "workbench.action.openGlobalKeybindings", - "winKey": "Ctrl+K Ctrl+S", - "status": "mapped", - "handler": "nppCommand", - "target": "IDM_SETTING_SHORTCUT_MAPPER", - "notes": "Open Shortcut Mapper.", - "runtime": true - }, - { - "section": "Preferences", - "subsection": "", - "label": "Select Color Theme", - "command": "workbench.action.selectTheme", - "winKey": "Ctrl+K Ctrl+T", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code theme chord.", - "runtime": true - }, - { - "section": "Chat", - "subsection": "", - "label": "Open Chat view", - "command": "workbench.action.chat.open", - "winKey": "Ctrl+Alt+I", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Chat", - "subsection": "", - "label": "Open chat in agent mode", - "command": "workbench.action.chat.openagent", - "winKey": "Ctrl+Shift+I", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Chat", - "subsection": "", - "label": "Open editor inline chat", - "command": "inlineChat.start", - "winKey": "Ctrl+I", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Chat", - "subsection": "", - "label": "Open terminal inline chat", - "command": "workbench.action.terminal.chat.start", - "winKey": "Ctrl+I", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Chat", - "subsection": "", - "label": "Open quick chat", - "command": "workbench.action.quickchat.toggle", - "winKey": "Ctrl+Shift+Alt+L", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Chat", - "subsection": "", - "label": "Open agent picker", - "command": "workbench.action.chat.openModePicker", - "winKey": "Ctrl+.", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Chat", - "subsection": "", - "label": "Open language model picker", - "command": "workbench.action.chat.openModelPicker", - "winKey": "Ctrl+Alt+.", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Chat", - "subsection": "", - "label": "New chat session", - "command": "workbench.action.chat.newChat", - "winKey": "Ctrl+N", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Chat", - "subsection": "", - "label": "Accept inline suggestion", - "command": "editor.action.inlineSuggest.commit", - "winKey": "Tab", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Debug", - "subsection": "", - "label": "Toggle Breakpoint", - "command": "editor.debug.action.toggleBreakpoint", - "winKey": "F9", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code breakpoint shortcut.", - "runtime": true - }, - { - "section": "Debug", - "subsection": "", - "label": "Start", - "command": "workbench.action.debug.start", - "winKey": "F5", - "status": "reserved-noop", - "handler": "reservedNoOp", - "target": null, - "notes": "Reserved so conflicting Notepad++ behavior does not fire for VS Code debug-start shortcut.", - "runtime": true - }, - { - "section": "Debug", - "subsection": "", - "label": "Continue", - "command": "workbench.action.debug.continue", - "winKey": "F5", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Debug", - "subsection": "", - "label": "Start (without debugging)", - "command": "workbench.action.debug.run", - "winKey": "Ctrl+F5", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Debug", - "subsection": "", - "label": "Pause", - "command": "workbench.action.debug.pause", - "winKey": "F6", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Debug", - "subsection": "", - "label": "Step Into", - "command": "workbench.action.debug.stepInto", - "winKey": "F11", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - }, - { - "section": "Tasks", - "subsection": "", - "label": "Run Build Task", - "command": "workbench.action.tasks.build", - "winKey": "Ctrl+Shift+B", - "status": "documented-unported", - "handler": "documentedUnported", - "target": null, - "notes": "", - "runtime": false - } - ] -} diff --git a/docs/VSCodeKeybindingCoverage.MD b/docs/VSCodeKeybindingCoverage.MD index ab8cb8f..5770ed5 100644 --- a/docs/VSCodeKeybindingCoverage.MD +++ b/docs/VSCodeKeybindingCoverage.MD @@ -4,9 +4,9 @@ Generated from https://code.visualstudio.com/docs/reference/default-keybindings. | Metric | Count | | --- | ---: | -| Total Windows-default bindings | 161 | -| Mapped | 82 | -| Reserved no-op | 39 | +| Total Windows-default bindings | 163 | +| Mapped | 86 | +| Reserved no-op | 37 | | Documented unported | 40 | ## Basic Editing @@ -21,8 +21,8 @@ Generated from https://code.visualstudio.com/docs/reference/default-keybindings. | `Ctrl+Shift+Enter` | Insert Line Above | `editor.action.insertLineBefore` | mapped | custom | Insert blank line above caret. | | `Alt+Down` | Move Line Down | `editor.action.moveLinesDownAction` | mapped | nppCommand | Move current line or selection down. | | `Alt+Up` | Move Line Up | `editor.action.moveLinesUpAction` | mapped | nppCommand | Move current line or selection up. | -| `Shift+Alt+Down` | Copy Line Down | `editor.action.copyLinesDownAction` | mapped | nppCommand | Duplicate line downward. | -| `Shift+Alt+Up` | Copy Line Up | `editor.action.copyLinesUpAction` | mapped | custom | Duplicate line, then move duplicate upward. | +| `Shift+Alt+Down` | Copy Line Down | `editor.action.copyLinesDownAction` | mapped | custom | Duplicate line downward and place the caret in the duplicate. | +| `Shift+Alt+Up` | Copy Line Up | `editor.action.copyLinesUpAction` | mapped | custom | Duplicate line upward and place the caret in the duplicate. | | `Ctrl+Z` | Undo | `undo` | mapped | nativePassThrough | Notepad++ already matches VS Code for undo. | | `Ctrl+Y` | Redo | `redo` | mapped | nativePassThrough | Notepad++ already matches VS Code for redo. | | `Ctrl+D` | Add Selection To Next Find Match | `editor.action.addSelectionToNextFindMatch` | mapped | nppCommand | Add next match to multiselection. | @@ -32,8 +32,8 @@ Generated from https://code.visualstudio.com/docs/reference/default-keybindings. | `Ctrl+Shift+L` | Select all occurrences of current selection | `editor.action.selectHighlights` | mapped | nppCommand | Select all current selection matches. | | `Ctrl+F2` | Select all occurrences of current word | `editor.action.changeAll` | mapped | nppCommand | Approximate current-word multi-select. | | `Ctrl+L` | Select current line | `expandLineSelection` | mapped | custom | Select current line without trailing newline. | -| `Ctrl+Alt+Down` | Insert Cursor Below | `editor.action.insertCursorBelow` | reserved-noop | reservedNoOp | Reserved so Notepad++ defaults do not hijack VS Code multi-cursor shortcut. | -| `Ctrl+Alt+Up` | Insert Cursor Above | `editor.action.insertCursorAbove` | reserved-noop | reservedNoOp | Reserved so Notepad++ defaults do not hijack VS Code multi-cursor shortcut. | +| `Ctrl+Alt+Down` | Insert Cursor Below | `editor.action.insertCursorBelow` | mapped | custom | Move with the shared vertical-cursor rules; add/select one caret below. Virtual-space alignment is configurable. | +| `Ctrl+Alt+Up` | Insert Cursor Above | `editor.action.insertCursorAbove` | mapped | custom | Move with the shared vertical-cursor rules; add/select one caret above. | | `Ctrl+Shift+\` | Jump to matching bracket | `editor.action.jumpToBracket` | mapped | nppCommand | Jump to matching brace. | | `Ctrl+]` | Indent Line | `editor.action.indentLines` | mapped | nppCommand | Approximate VS Code indent with Notepad++ indent command. | | `Ctrl+[` | Outdent Line | `editor.action.outdentLines` | mapped | nppCommand | Approximate VS Code outdent with Notepad++ unindent command. | @@ -66,6 +66,8 @@ Generated from https://code.visualstudio.com/docs/reference/default-keybindings. | `Alt+W` | Toggle Find Whole Word | `toggleFindWholeWord` | documented-unported | documentedUnported | | | `Ctrl+M` | Toggle Use of Tab Key for Setting Focus | `editor.action.toggleTabFocusMode` | documented-unported | documentedUnported | | | `Alt+Z` | Toggle Word Wrap | `editor.action.toggleWordWrap` | mapped | nppCommand | Toggle word wrap. | +| `Ctrl+Alt+Shift+Down` | Column Select Down | `editor.action.cursorColumnSelectDown` | mapped | custom | Move with the shared vertical-cursor rules; rebuild the anchored column-selection range downward. Undocumented on the VS Code default keybindings page, but available in the VS Code Keyboard Shortcuts editor. | +| `Ctrl+Alt+Shift+Up` | Column Select Up | `editor.action.cursorColumnSelectUp` | mapped | custom | Move with the shared vertical-cursor rules; rebuild the anchored column-selection range upward. Undocumented on the VS Code default keybindings page, but available in the VS Code Keyboard Shortcuts editor. | ## Rich Languages Editing | Key | Command | VS Code command id | Status | Handling | Notes | diff --git a/scripts/package-release.ps1 b/scripts/package-release.ps1 index c191aa6..cbe7148 100644 --- a/scripts/package-release.ps1 +++ b/scripts/package-release.ps1 @@ -137,7 +137,6 @@ function Package-Platform { } $stageRoot = Join-Path $DistRoot ("_package-{0}" -f $Spec.Name) - $docDir = Join-Path $stageRoot "doc" $zipPath = Join-Path $DistRoot ("VSCodeKeymapNpp-{0}-{1}.zip" -f $ResolvedVersion, $Spec.Name) if (Test-Path -LiteralPath $stageRoot) { @@ -149,7 +148,6 @@ function Package-Platform { New-Item -ItemType Directory -Path $stageRoot -Force | Out-Null Copy-Item -LiteralPath $dllPath -Destination (Join-Path $stageRoot "VSCodeKeymapNpp.dll") - Copy-Docs -Destination $docDir Compress-Archive ` -Path (Join-Path $stageRoot "*") ` diff --git a/scripts/sync-vscode-keybindings.ps1 b/scripts/sync-vscode-keybindings.ps1 index 1d46492..5ee724e 100644 --- a/scripts/sync-vscode-keybindings.ps1 +++ b/scripts/sync-vscode-keybindings.ps1 @@ -3,7 +3,6 @@ param( [string]$SourceUrl = "https://code.visualstudio.com/docs/reference/default-keybindings", [string]$SourceHtmlPath, [string]$MappingsPath, - [string]$OutputJsonPath, [string]$OutputCoveragePath, [string]$OutputBindingsPath ) @@ -19,9 +18,6 @@ if (-not $SourceHtmlPath) { if (-not $MappingsPath) { $MappingsPath = Join-Path $repoRoot "data\keybinding-mappings.json" } -if (-not $OutputJsonPath) { - $OutputJsonPath = Join-Path $repoRoot "data\vscode-default-keybindings.windows.json" -} if (-not $OutputCoveragePath) { $OutputCoveragePath = Join-Path $repoRoot "docs\VSCodeKeybindingCoverage.MD" } @@ -184,10 +180,43 @@ function Write-GeneratedBindings { $Stroke.Vk } + function Format-WideString { + param([AllowNull()][string]$Value) + + if ([string]::IsNullOrEmpty($Value)) { + return 'L""' + } + + $escaped = $Value.Replace('\', '\\').Replace('"', '\"').Replace("`r", '\r').Replace("`n", '\n').Replace("`t", '\t') + return 'L"{0}"' -f $escaped + } + + function Format-Bool { + param([bool]$Value) + + return $Value.ToString().ToLowerInvariant() + } + + $referenceRows = New-Object System.Collections.Generic.List[string] $shortcutRows = New-Object System.Collections.Generic.List[string] $chordRows = New-Object System.Collections.Generic.List[string] - foreach ($binding in $Bindings) { + for ($referenceIndex = 0; $referenceIndex -lt $Bindings.Count; $referenceIndex++) { + $binding = $Bindings[$referenceIndex] + $referenceRows.Add( + (' {{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}}},' -f ` + (Format-Bool -Value $binding.runtime), ` + (Format-WideString -Value $binding.section), ` + (Format-WideString -Value $binding.subsection), ` + (Format-WideString -Value $binding.label), ` + (Format-WideString -Value $binding.command), ` + (Format-WideString -Value $binding.winKey), ` + (Format-WideString -Value $binding.status), ` + (Format-WideString -Value $binding.handler), ` + (Format-WideString -Value $binding.target), ` + (Format-WideString -Value $binding.notes)) + ) | Out-Null + if (-not $binding.Runtime) { continue } @@ -198,10 +227,11 @@ function Write-GeneratedBindings { if ($strokes.Count -eq 1) { $stroke = $strokes[0] $shortcutRows.Add( - (' {{{0}, {1}, {2}}}, // {3} -> {4}' -f ` + (' {{{0}, {1}, {2}, {3}}}, // {4} -> {5}' -f ` (Format-Stroke -Stroke $stroke), ` $action.Kind, ` $action.Id, ` + $referenceIndex, ` $binding.winKey, ` $binding.command) ) | Out-Null @@ -211,11 +241,12 @@ function Write-GeneratedBindings { $first = $strokes[0] $second = $strokes[1] $chordRows.Add( - (' {{{0}, {1}, {2}, {3}}}, // {4} -> {5}' -f ` + (' {{{0}, {1}, {2}, {3}, {4}}}, // {5} -> {6}' -f ` (Format-Stroke -Stroke $first), ` (Format-Stroke -Stroke $second), ` $action.Kind, ` $action.Id, ` + $referenceIndex, ` $binding.winKey, ` $binding.command) ) | Out-Null @@ -229,6 +260,15 @@ function Write-GeneratedBindings { "constexpr size_t kReservedNoOpBindingCount = $($Counts.reserved);" "constexpr size_t kDocumentedUnportedBindingCount = $($Counts.unported);" "" + "const std::vector kBindingReferences = {" + ) + + if ($referenceRows.Count -gt 0) { + $content += $referenceRows + } + $content += @( + "};" + "" "const std::vector kShortcutBindings = {" ) @@ -340,6 +380,43 @@ function Get-DefaultBindingRecord { } } +function Get-ManifestOnlyBindingRecord { + param([object]$Mapping) + + $requiredFields = @("section", "label") + foreach ($field in $requiredFields) { + if (-not ($Mapping.PSObject.Properties.Name -contains $field) -or [string]::IsNullOrWhiteSpace($Mapping.$field)) { + throw "Manifest-only binding requires '$field': $($Mapping.command) / $($Mapping.winKey)" + } + } + + $target = $null + $notes = "" + $subsection = "" + if ($Mapping.PSObject.Properties.Name -contains "target") { + $target = $Mapping.target + } + if ($Mapping.PSObject.Properties.Name -contains "notes") { + $notes = $Mapping.notes + } + if ($Mapping.PSObject.Properties.Name -contains "subsection") { + $subsection = $Mapping.subsection + } + + return [ordered]@{ + section = $Mapping.section + subsection = $subsection + label = $Mapping.label + command = $Mapping.command + winKey = $Mapping.winKey + status = Get-StatusFromHandler -Handler $Mapping.handler + handler = $Mapping.handler + target = $target + notes = $notes + runtime = Get-RuntimeFlag -Handler $Mapping.handler + } +} + if ($RefreshSource -or (-not (Test-Path -LiteralPath $SourceHtmlPath))) { New-Item -ItemType Directory -Path (Split-Path -Parent $SourceHtmlPath) -Force | Out-Null curl.exe --silent --show-error -L $SourceUrl -o $SourceHtmlPath | Out-Null @@ -411,12 +488,14 @@ foreach ($row in $parsedRows) { $mappingDoc = Get-Content -Raw $MappingsPath | ConvertFrom-Json $mappingLookup = @{} +$mappingOrder = New-Object System.Collections.Generic.List[string] foreach ($item in $mappingDoc.bindings) { $key = "$($item.command)`n$($item.winKey)" if ($mappingLookup.ContainsKey($key)) { throw "Duplicate mapping manifest entry for $($item.command) / $($item.winKey)." } $mappingLookup[$key] = $item + $mappingOrder.Add($key) | Out-Null } $resolvedBindings = New-Object System.Collections.Generic.List[object] @@ -442,10 +521,25 @@ foreach ($binding in $dedupedRows) { $resolvedBindings.Add([pscustomobject]$record) | Out-Null } -foreach ($lookupKey in $mappingLookup.Keys) { +foreach ($lookupKey in $mappingOrder) { $match = $resolvedBindings | Where-Object { "$($_.command)`n$($_.winKey)" -eq $lookupKey } if (-not $match) { - throw "Manifest entry does not match current source binding: $lookupKey" + $record = [pscustomobject](Get-ManifestOnlyBindingRecord -Mapping $mappingLookup[$lookupKey]) + $insertIndex = -1 + for ($index = $resolvedBindings.Count - 1; $index -ge 0; $index--) { + if (($resolvedBindings[$index].section -eq $record.section) -and + ($resolvedBindings[$index].subsection -eq $record.subsection)) { + $insertIndex = $index + 1 + break + } + } + + if ($insertIndex -ge 0) { + $resolvedBindings.Insert($insertIndex, $record) + } + else { + $resolvedBindings.Add($record) | Out-Null + } } } @@ -481,25 +575,13 @@ $counts = @{ unported = @($resolvedBindings | Where-Object { $_.status -eq "documented-unported" }).Count } -$jsonDoc = [ordered]@{ - source = [ordered]@{ - url = $SourceUrl - msDate = $sourceDate - } - counts = $counts - bindings = $resolvedBindings -} - -New-Item -ItemType Directory -Path (Split-Path -Parent $OutputJsonPath) -Force | Out-Null New-Item -ItemType Directory -Path (Split-Path -Parent $OutputCoveragePath) -Force | Out-Null New-Item -ItemType Directory -Path (Split-Path -Parent $OutputBindingsPath) -Force | Out-Null -$jsonDoc | ConvertTo-Json -Depth 10 | Set-Content -Path $OutputJsonPath -Encoding UTF8 Write-CoverageReport -Path $OutputCoveragePath -Bindings $resolvedBindings -Counts $counts Write-GeneratedBindings -Path $OutputBindingsPath -Bindings $resolvedBindings -Counts $counts Write-Output "[OK] Parsed $($counts.total) Windows-default VS Code bindings." Write-Output "[OK] Mapped: $($counts.mapped); reserved: $($counts.reserved); documented-unported: $($counts.unported)." -Write-Output "[OK] Wrote $OutputJsonPath" Write-Output "[OK] Wrote $OutputCoveragePath" Write-Output "[OK] Wrote $OutputBindingsPath" diff --git a/src/GeneratedBindings.inc b/src/GeneratedBindings.inc index 4ff45d9..1f3e268 100644 --- a/src/GeneratedBindings.inc +++ b/src/GeneratedBindings.inc @@ -1,124 +1,292 @@ // Generated by scripts/sync-vscode-keybindings.ps1. Do not edit by hand. -constexpr size_t kVsCodeSourceBindingCount = 161; -constexpr size_t kMappedBindingCount = 82; -constexpr size_t kReservedNoOpBindingCount = 39; +constexpr size_t kVsCodeSourceBindingCount = 163; +constexpr size_t kMappedBindingCount = 86; +constexpr size_t kReservedNoOpBindingCount = 37; constexpr size_t kDocumentedUnportedBindingCount = 40; +const std::vector kBindingReferences = { + {true, L"Basic Editing", L"", L"Cut line (empty selection)", L"editor.action.clipboardCutAction", L"Ctrl+X", L"mapped", L"custom", L"CutAllowLine", L"Cut whole line when selection is empty, like VS Code."}, + {true, L"Basic Editing", L"", L"Copy line (empty selection)", L"editor.action.clipboardCopyAction", L"Ctrl+C", L"mapped", L"custom", L"CopyAllowLine", L"Copy whole line when selection is empty, like VS Code."}, + {false, L"Basic Editing", L"", L"Paste", L"editor.action.clipboardPasteAction", L"Ctrl+V", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for paste."}, + {true, L"Basic Editing", L"", L"Delete Line", L"editor.action.deleteLines", L"Ctrl+Shift+K", L"mapped", L"sciCommand", L"SCI_LINEDELETE", L"Delete current line when selection is empty, matching VS Code behavior closely."}, + {true, L"Basic Editing", L"", L"Insert Line Below", L"editor.action.insertLineAfter", L"Ctrl+Enter", L"mapped", L"custom", L"InsertLineBelow", L"Insert blank line below caret."}, + {true, L"Basic Editing", L"", L"Insert Line Above", L"editor.action.insertLineBefore", L"Ctrl+Shift+Enter", L"mapped", L"custom", L"InsertLineAbove", L"Insert blank line above caret."}, + {true, L"Basic Editing", L"", L"Move Line Down", L"editor.action.moveLinesDownAction", L"Alt+Down", L"mapped", L"nppCommand", L"IDM_EDIT_LINE_DOWN", L"Move current line or selection down."}, + {true, L"Basic Editing", L"", L"Move Line Up", L"editor.action.moveLinesUpAction", L"Alt+Up", L"mapped", L"nppCommand", L"IDM_EDIT_LINE_UP", L"Move current line or selection up."}, + {true, L"Basic Editing", L"", L"Copy Line Down", L"editor.action.copyLinesDownAction", L"Shift+Alt+Down", L"mapped", L"custom", L"DuplicateLineDown", L"Duplicate line downward and place the caret in the duplicate."}, + {true, L"Basic Editing", L"", L"Copy Line Up", L"editor.action.copyLinesUpAction", L"Shift+Alt+Up", L"mapped", L"custom", L"DuplicateLineUp", L"Duplicate line upward and place the caret in the duplicate."}, + {false, L"Basic Editing", L"", L"Undo", L"undo", L"Ctrl+Z", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for undo."}, + {false, L"Basic Editing", L"", L"Redo", L"redo", L"Ctrl+Y", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for redo."}, + {true, L"Basic Editing", L"", L"Add Selection To Next Find Match", L"editor.action.addSelectionToNextFindMatch", L"Ctrl+D", L"mapped", L"nppCommand", L"IDM_EDIT_MULTISELECTNEXT", L"Add next match to multiselection."}, + {true, L"Basic Editing", L"", L"Move Last Selection To Next Find Match", L"editor.action.moveSelectionToNextFindMatch", L"Ctrl+K Ctrl+D", L"mapped", L"nppCommand", L"IDM_EDIT_MULTISELECTSSKIP", L"Skip current occurrence in multi-select flow."}, + {true, L"Basic Editing", L"", L"Undo last cursor operation", L"cursorUndo", L"Ctrl+U", L"mapped", L"nppCommand", L"IDM_EDIT_MULTISELECTUNDO", L"Undo last multi-cursor selection step."}, + {true, L"Basic Editing", L"", L"Insert cursor at end of each line selected", L"editor.action.insertCursorAtEndOfEachLineSelected", L"Shift+Alt+I", L"reserved-noop", L"reservedNoOp", L"", L"Multiple-caret insertion per line is not implemented safely in Notepad++."}, + {true, L"Basic Editing", L"", L"Select all occurrences of current selection", L"editor.action.selectHighlights", L"Ctrl+Shift+L", L"mapped", L"nppCommand", L"IDM_EDIT_MULTISELECTALL", L"Select all current selection matches."}, + {true, L"Basic Editing", L"", L"Select all occurrences of current word", L"editor.action.changeAll", L"Ctrl+F2", L"mapped", L"nppCommand", L"IDM_EDIT_MULTISELECTALLWHOLEWORD", L"Approximate current-word multi-select."}, + {true, L"Basic Editing", L"", L"Select current line", L"expandLineSelection", L"Ctrl+L", L"mapped", L"custom", L"SelectCurrentLine", L"Select current line without trailing newline."}, + {true, L"Basic Editing", L"", L"Insert Cursor Below", L"editor.action.insertCursorBelow", L"Ctrl+Alt+Down", L"mapped", L"custom", L"InsertCursorBelow", L"Move with the shared vertical-cursor rules; add/select one caret below. Virtual-space alignment is configurable."}, + {true, L"Basic Editing", L"", L"Insert Cursor Above", L"editor.action.insertCursorAbove", L"Ctrl+Alt+Up", L"mapped", L"custom", L"InsertCursorAbove", L"Move with the shared vertical-cursor rules; add/select one caret above."}, + {true, L"Basic Editing", L"", L"Jump to matching bracket", L"editor.action.jumpToBracket", L"Ctrl+Shift+\\", L"mapped", L"nppCommand", L"IDM_SEARCH_GOTOMATCHINGBRACE", L"Jump to matching brace."}, + {true, L"Basic Editing", L"", L"Indent Line", L"editor.action.indentLines", L"Ctrl+]", L"mapped", L"nppCommand", L"IDM_EDIT_INS_TAB", L"Approximate VS Code indent with Notepad++ indent command."}, + {true, L"Basic Editing", L"", L"Outdent Line", L"editor.action.outdentLines", L"Ctrl+[", L"mapped", L"nppCommand", L"IDM_EDIT_RMV_TAB", L"Approximate VS Code outdent with Notepad++ unindent command."}, + {false, L"Basic Editing", L"", L"Go to Beginning of Line", L"cursorHome", L"Home", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for line-home movement."}, + {false, L"Basic Editing", L"", L"Go to End of Line", L"cursorEnd", L"End", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for line-end movement."}, + {true, L"Basic Editing", L"", L"Go to End of File", L"cursorBottom", L"Ctrl+End", L"mapped", L"sciCommand", L"SCI_DOCUMENTEND", L"Jump to document end."}, + {true, L"Basic Editing", L"", L"Go to Beginning of File", L"cursorTop", L"Ctrl+Home", L"mapped", L"sciCommand", L"SCI_DOCUMENTSTART", L"Jump to document start."}, + {true, L"Basic Editing", L"", L"Scroll Line Down", L"scrollLineDown", L"Ctrl+Down", L"mapped", L"sciCommand", L"SCI_LINESCROLLDOWN", L"Scroll without moving caret."}, + {true, L"Basic Editing", L"", L"Scroll Line Up", L"scrollLineUp", L"Ctrl+Up", L"mapped", L"sciCommand", L"SCI_LINESCROLLUP", L"Scroll without moving caret."}, + {true, L"Basic Editing", L"", L"Scroll Page Down", L"scrollPageDown", L"Alt+PageDown", L"mapped", L"custom", L"ScrollPageDownNoCaret", L"Scroll a page without moving caret."}, + {true, L"Basic Editing", L"", L"Scroll Page Up", L"scrollPageUp", L"Alt+PageUp", L"mapped", L"custom", L"ScrollPageUpNoCaret", L"Scroll a page without moving caret."}, + {true, L"Basic Editing", L"", L"Fold (collapse) region", L"editor.fold", L"Ctrl+Shift+[", L"mapped", L"nppCommand", L"IDM_VIEW_FOLD_CURRENT", L"Fold current region."}, + {true, L"Basic Editing", L"", L"Unfold (uncollapse) region", L"editor.unfold", L"Ctrl+Shift+]", L"mapped", L"nppCommand", L"IDM_VIEW_UNFOLD_CURRENT", L"Unfold current region."}, + {true, L"Basic Editing", L"", L"Toggle Fold region", L"editor.toggleFold", L"Ctrl+K Ctrl+L", L"mapped", L"custom", L"ToggleFoldAtCaret", L"Toggle fold header at caret, or nearest fold parent."}, + {true, L"Basic Editing", L"", L"Fold (collapse) all subregions", L"editor.foldRecursively", L"Ctrl+K Ctrl+[", L"reserved-noop", L"reservedNoOp", L"", L"Reserved chord; exact recursive fold not implemented."}, + {true, L"Basic Editing", L"", L"Unfold (uncollapse) all subregions", L"editor.unfoldRecursively", L"Ctrl+K Ctrl+]", L"reserved-noop", L"reservedNoOp", L"", L"Reserved chord; exact recursive unfold not implemented."}, + {true, L"Basic Editing", L"", L"Fold (collapse) all regions", L"editor.foldAll", L"Ctrl+K Ctrl+0", L"mapped", L"nppCommand", L"IDM_VIEW_FOLDALL", L"Fold all regions."}, + {true, L"Basic Editing", L"", L"Unfold (uncollapse) all regions", L"editor.unfoldAll", L"Ctrl+K Ctrl+J", L"mapped", L"nppCommand", L"IDM_VIEW_UNFOLDALL", L"Unfold all regions."}, + {true, L"Basic Editing", L"", L"Add Line Comment", L"editor.action.addCommentLine", L"Ctrl+K Ctrl+C", L"mapped", L"nppCommand", L"IDM_EDIT_STREAM_COMMENT", L"Force comment selected lines."}, + {true, L"Basic Editing", L"", L"Remove Line Comment", L"editor.action.removeCommentLine", L"Ctrl+K Ctrl+U", L"mapped", L"nppCommand", L"IDM_EDIT_STREAM_UNCOMMENT", L"Force uncomment selected lines."}, + {true, L"Basic Editing", L"", L"Toggle Line Comment", L"editor.action.commentLine", L"Ctrl+/", L"mapped", L"custom", L"ToggleStreamComment", L"Toggle line-style stream comment based on current selection state."}, + {true, L"Basic Editing", L"", L"Toggle Block Comment", L"editor.action.blockComment", L"Shift+Alt+A", L"mapped", L"nppCommand", L"IDM_EDIT_BLOCK_COMMENT", L"Use Notepad++ block comment command."}, + {false, L"Basic Editing", L"", L"Find", L"actions.find", L"Ctrl+F", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for find."}, + {false, L"Basic Editing", L"", L"Replace", L"editor.action.startFindReplaceAction", L"Ctrl+H", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for replace."}, + {false, L"Basic Editing", L"", L"Find Next", L"editor.action.nextMatchFindAction", L"Enter", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Basic Editing", L"", L"Find Previous", L"editor.action.previousMatchFindAction", L"Shift+Enter", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Basic Editing", L"", L"Select All Occurrences of Find Match", L"editor.action.selectAllMatches", L"Alt+Enter", L"mapped", L"nppCommand", L"IDM_EDIT_MULTISELECTALL", L"Select all find matches."}, + {false, L"Basic Editing", L"", L"Toggle Find Case Sensitive", L"toggleFindCaseSensitive", L"Alt+C", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Basic Editing", L"", L"Toggle Find Regex", L"toggleFindRegex", L"Alt+R", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Basic Editing", L"", L"Toggle Find Whole Word", L"toggleFindWholeWord", L"Alt+W", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Basic Editing", L"", L"Toggle Use of Tab Key for Setting Focus", L"editor.action.toggleTabFocusMode", L"Ctrl+M", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Basic Editing", L"", L"Toggle Word Wrap", L"editor.action.toggleWordWrap", L"Alt+Z", L"mapped", L"nppCommand", L"IDM_VIEW_WRAP", L"Toggle word wrap."}, + {true, L"Basic Editing", L"", L"Column Select Down", L"editor.action.cursorColumnSelectDown", L"Ctrl+Alt+Shift+Down", L"mapped", L"custom", L"CursorColumnSelectDown", L"Move with the shared vertical-cursor rules; rebuild the anchored column-selection range downward. Undocumented on the VS Code default keybindings page, but available in the VS Code Keyboard Shortcuts editor."}, + {true, L"Basic Editing", L"", L"Column Select Up", L"editor.action.cursorColumnSelectUp", L"Ctrl+Alt+Shift+Up", L"mapped", L"custom", L"CursorColumnSelectUp", L"Move with the shared vertical-cursor rules; rebuild the anchored column-selection range upward. Undocumented on the VS Code default keybindings page, but available in the VS Code Keyboard Shortcuts editor."}, + {true, L"Rich Languages Editing", L"", L"Trigger Suggest", L"editor.action.triggerSuggest", L"Ctrl+Space", L"mapped", L"nppCommand", L"IDM_EDIT_AUTOCOMPLETE", L"Open autocomplete."}, + {true, L"Rich Languages Editing", L"", L"Trigger Parameter Hints", L"editor.action.triggerParameterHints", L"Ctrl+Shift+Space", L"mapped", L"nppCommand", L"IDM_EDIT_FUNCCALLTIP", L"Open function call tip."}, + {true, L"Rich Languages Editing", L"", L"Format Document", L"editor.action.formatDocument", L"Shift+Alt+F", L"reserved-noop", L"reservedNoOp", L"", L"Formatting depends on plugins/language support, so strict mode reserves it."}, + {true, L"Rich Languages Editing", L"", L"Format Selection", L"editor.action.formatSelection", L"Ctrl+K Ctrl+F", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code format-selection chord."}, + {true, L"Rich Languages Editing", L"", L"Go to Definition", L"editor.action.revealDefinition", L"F12", L"reserved-noop", L"reservedNoOp", L"", L"No built-in go-to-definition equivalent."}, + {true, L"Rich Languages Editing", L"", L"Show Hover", L"editor.action.showHover", L"Ctrl+K Ctrl+I", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code hover chord."}, + {true, L"Rich Languages Editing", L"", L"Peek Definition", L"editor.action.peekDefinition", L"Alt+F12", L"reserved-noop", L"reservedNoOp", L"", L"No peek-definition equivalent."}, + {true, L"Rich Languages Editing", L"", L"Open Definition to the Side", L"editor.action.revealDefinitionAside", L"Ctrl+K F12", L"reserved-noop", L"reservedNoOp", L"", L"No open-definition-side equivalent."}, + {true, L"Rich Languages Editing", L"", L"Quick Fix", L"editor.action.quickFix", L"Ctrl+.", L"reserved-noop", L"reservedNoOp", L"", L"No quick-fix provider equivalent."}, + {true, L"Rich Languages Editing", L"", L"Go to References", L"editor.action.goToReferences", L"Shift+F12", L"reserved-noop", L"reservedNoOp", L"", L"No find-references equivalent."}, + {true, L"Rich Languages Editing", L"", L"Rename Symbol", L"editor.action.rename", L"F2", L"reserved-noop", L"reservedNoOp", L"", L"No language-aware rename equivalent."}, + {false, L"Rich Languages Editing", L"", L"Replace with Next Value", L"editor.action.inPlaceReplace.down", L"Ctrl+Shift+.", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Rich Languages Editing", L"", L"Replace with Previous Value", L"editor.action.inPlaceReplace.up", L"Ctrl+Shift+,", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Rich Languages Editing", L"", L"Expand AST Selection", L"editor.action.smartSelect.expand", L"Shift+Alt+Right", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Rich Languages Editing", L"", L"Shrink AST Selection", L"editor.action.smartSelect.shrink", L"Shift+Alt+Left", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Rich Languages Editing", L"", L"Trim Trailing Whitespace", L"editor.action.trimTrailingWhitespace", L"Ctrl+K Ctrl+X", L"mapped", L"nppCommand", L"IDM_EDIT_TRIMTRAILING", L"Trim trailing whitespace."}, + {true, L"Rich Languages Editing", L"", L"Change Language Mode", L"workbench.action.editor.changeLanguageMode", L"Ctrl+K M", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code language-mode chord."}, + {true, L"Navigation", L"", L"Show All Symbols", L"workbench.action.showAllSymbols", L"Ctrl+T", L"mapped", L"nppCommand", L"IDM_VIEW_SWITCHTO_FUNC_LIST", L"Approximate workspace symbol search with function list."}, + {true, L"Navigation", L"", L"Go to Line...", L"workbench.action.gotoLine", L"Ctrl+G", L"mapped", L"nppCommand", L"IDM_SEARCH_GOTOLINE", L"Go to line."}, + {true, L"Navigation", L"", L"Go to File..., Quick Open", L"workbench.action.quickOpen", L"Ctrl+P", L"mapped", L"nppCommand", L"IDM_FILE_OPEN", L"Approximate Quick Open with file-open dialog."}, + {true, L"Navigation", L"", L"Go to Symbol...", L"workbench.action.gotoSymbol", L"Ctrl+Shift+O", L"mapped", L"nppCommand", L"IDM_VIEW_SWITCHTO_FUNC_LIST", L"Approximate symbol picker with function list panel."}, + {true, L"Navigation", L"", L"Show Problems", L"workbench.actions.view.problems", L"Ctrl+Shift+M", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code problems shortcut."}, + {true, L"Navigation", L"", L"Go to Next Error or Warning", L"editor.action.marker.nextInFiles", L"F8", L"reserved-noop", L"reservedNoOp", L"", L"No Problems panel equivalent."}, + {true, L"Navigation", L"", L"Go to Previous Error or Warning", L"editor.action.marker.prevInFiles", L"Shift+F8", L"reserved-noop", L"reservedNoOp", L"", L"No Problems panel equivalent."}, + {true, L"Navigation", L"", L"Show All Commands", L"workbench.action.showCommands", L"Ctrl+Shift+P", L"reserved-noop", L"reservedNoOp", L"", L"No Command Palette equivalent."}, + {true, L"Navigation", L"", L"Navigate Editor Group History", L"workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup", L"Ctrl+Tab", L"mapped", L"nppCommand", L"IDM_VIEW_TAB_NEXT", L"Cycle tabs forward."}, + {true, L"Navigation", L"", L"Go Back", L"workbench.action.navigateBack", L"Alt+Left", L"reserved-noop", L"reservedNoOp", L"", L"No stable back-stack equivalent."}, + {false, L"Navigation", L"", L"Go back in Quick Input", L"workbench.action.quickInputBack", L"Alt+Left", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Navigation", L"", L"Go Forward", L"workbench.action.navigateForward", L"Alt+Right", L"reserved-noop", L"reservedNoOp", L"", L"No stable forward-stack equivalent."}, + {false, L"Navigation", L"", L"Focus Breadcrumbs", L"breadcrumbs.focus", L"Ctrl+Shift+;", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Navigation", L"", L"Focus and Select Breadcrumbs", L"breadcrumbs.focusAndSelect", L"Ctrl+Shift+.", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Editor/Window Management", L"", L"New Window", L"workbench.action.newWindow", L"Ctrl+Shift+N", L"mapped", L"nppCommand", L"IDM_VIEW_GOTO_NEW_INSTANCE", L"Approximate new window with new Notepad++ instance."}, + {false, L"Editor/Window Management", L"", L"Close Window", L"workbench.action.closeWindow", L"Alt+F4", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Editor/Window Management", L"", L"Close Editor", L"workbench.action.closeActiveEditor", L"Ctrl+F4", L"mapped", L"nppCommand", L"IDM_FILE_CLOSE", L"Close active file."}, + {true, L"Editor/Window Management", L"", L"Close Folder", L"workbench.action.closeFolder", L"Ctrl+K F", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code close-folder chord."}, + {true, L"Editor/Window Management", L"", L"Split Editor", L"workbench.action.splitEditor", L"Ctrl+\\", L"mapped", L"nppCommand", L"IDM_VIEW_GOTO_ANOTHER_VIEW", L"Approximate split editor by moving document to the other view."}, + {true, L"Editor/Window Management", L"", L"Focus into First Editor Group", L"workbench.action.focusFirstEditorGroup", L"Ctrl+1", L"mapped", L"nppCommand", L"IDM_VIEW_TAB1", L"Approximate first editor group focus with first tab selection."}, + {true, L"Editor/Window Management", L"", L"Focus into Second Editor Group", L"workbench.action.focusSecondEditorGroup", L"Ctrl+2", L"mapped", L"nppCommand", L"IDM_VIEW_TAB2", L"Approximate second editor group focus with second tab selection."}, + {true, L"Editor/Window Management", L"", L"Focus into Third Editor Group", L"workbench.action.focusThirdEditorGroup", L"Ctrl+3", L"mapped", L"nppCommand", L"IDM_VIEW_TAB3", L"Approximate third editor group focus with third tab selection."}, + {true, L"Editor/Window Management", L"", L"Focus into Editor Group on the Left", L"workbench.action.focusLeftGroup", L"Ctrl+K Ctrl+Left", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code focus-group chord."}, + {true, L"Editor/Window Management", L"", L"Focus into Editor Group on the Right", L"workbench.action.focusRightGroup", L"Ctrl+K Ctrl+Right", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code focus-group chord."}, + {true, L"Editor/Window Management", L"", L"Move Editor Left", L"workbench.action.moveEditorLeftInGroup", L"Ctrl+Shift+PageUp", L"mapped", L"nppCommand", L"IDM_VIEW_TAB_MOVEBACKWARD", L"Approximate move editor left with move tab left."}, + {true, L"Editor/Window Management", L"", L"Move Editor Right", L"workbench.action.moveEditorRightInGroup", L"Ctrl+Shift+PageDown", L"mapped", L"nppCommand", L"IDM_VIEW_TAB_MOVEFORWARD", L"Approximate move editor right with move tab right."}, + {true, L"Editor/Window Management", L"", L"Move Active Editor Group Left", L"workbench.action.moveActiveEditorGroupLeft", L"Ctrl+K Left", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code move-group chord."}, + {true, L"Editor/Window Management", L"", L"Move Active Editor Group Right", L"workbench.action.moveActiveEditorGroupRight", L"Ctrl+K Right", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code move-group chord."}, + {true, L"Editor/Window Management", L"", L"Move Editor into Next Group", L"workbench.action.moveEditorToNextGroup", L"Ctrl+Alt+Right", L"mapped", L"nppCommand", L"IDM_VIEW_GOTO_ANOTHER_VIEW", L"Approximate move editor to next group by moving document to the other view."}, + {true, L"Editor/Window Management", L"", L"Move Editor into Previous Group", L"workbench.action.moveEditorToPreviousGroup", L"Ctrl+Alt+Left", L"mapped", L"nppCommand", L"IDM_VIEW_GOTO_ANOTHER_VIEW", L"Approximate move editor to previous group by moving document to the other view."}, + {false, L"File Management", L"", L"New File", L"workbench.action.files.newUntitledFile", L"Ctrl+N", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for new file."}, + {false, L"File Management", L"", L"Open File...", L"workbench.action.files.openFile", L"Ctrl+O", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for open file."}, + {false, L"File Management", L"", L"Save", L"workbench.action.files.save", L"Ctrl+S", L"mapped", L"nativePassThrough", L"", L"Notepad++ already matches VS Code for save."}, + {true, L"File Management", L"", L"Save All", L"saveAll", L"Ctrl+K S", L"mapped", L"nppCommand", L"IDM_FILE_SAVEALL", L"Save all open files."}, + {true, L"File Management", L"", L"Save As...", L"workbench.action.files.saveAs", L"Ctrl+Shift+S", L"mapped", L"nppCommand", L"IDM_FILE_SAVEAS", L"Save current file as."}, + {true, L"File Management", L"", L"Close Group", L"workbench.action.closeEditorsInGroup", L"Ctrl+K W", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code close-group chord."}, + {true, L"File Management", L"", L"Close All", L"workbench.action.closeAllEditors", L"Ctrl+K Ctrl+W", L"mapped", L"nppCommand", L"IDM_FILE_CLOSEALL", L"Close all files."}, + {true, L"File Management", L"", L"Reopen Closed Editor", L"workbench.action.reopenClosedEditor", L"Ctrl+Shift+T", L"mapped", L"nppCommand", L"IDM_FILE_RESTORELASTCLOSEDFILE", L"Restore last closed file."}, + {true, L"File Management", L"", L"Keep Open", L"workbench.action.keepEditor", L"Ctrl+K Enter", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code keep-open chord."}, + {true, L"File Management", L"", L"Copy Path of Active File", L"workbench.action.files.copyPathOfActiveFile", L"Ctrl+K P", L"mapped", L"nppCommand", L"IDM_EDIT_FULLPATHTOCLIP", L"Copy active file path."}, + {true, L"File Management", L"", L"Reveal Active File in Windows", L"workbench.action.files.revealActiveFileInWindows", L"Ctrl+K R", L"mapped", L"nppCommand", L"IDM_EDIT_OPENINFOLDER", L"Reveal active file in Explorer."}, + {true, L"Display", L"", L"Toggle Full Screen", L"workbench.action.toggleFullScreen", L"F11", L"mapped", L"nppCommand", L"IDM_VIEW_FULLSCREENTOGGLE", L"Toggle fullscreen."}, + {true, L"Display", L"", L"Toggle Zen Mode", L"workbench.action.toggleZenMode", L"Ctrl+K Z", L"mapped", L"nppCommand", L"IDM_VIEW_DISTRACTIONFREE", L"Approximate VS Code Zen Mode with Notepad++ distraction-free mode."}, + {false, L"Display", L"", L"Leave Zen Mode", L"workbench.action.exitZenMode", L"Escape Escape", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Display", L"", L"Zoom in", L"workbench.action.zoomIn", L"Ctrl+=", L"mapped", L"nppCommand", L"IDM_VIEW_ZOOMIN", L"Zoom in."}, + {true, L"Display", L"", L"Zoom out", L"workbench.action.zoomOut", L"Ctrl+-", L"mapped", L"nppCommand", L"IDM_VIEW_ZOOMOUT", L"Zoom out."}, + {true, L"Display", L"", L"Reset Zoom", L"workbench.action.zoomReset", L"Ctrl+Numpad0", L"mapped", L"nppCommand", L"IDM_VIEW_ZOOMRESTORE", L"Reset zoom."}, + {true, L"Display", L"", L"Toggle Sidebar Visibility", L"workbench.action.toggleSidebarVisibility", L"Ctrl+B", L"mapped", L"nppCommand", L"IDM_VIEW_FILEBROWSER", L"Approximate sidebar toggle with file browser panel."}, + {true, L"Display", L"", L"Show Explorer / Toggle Focus", L"workbench.view.explorer", L"Ctrl+Shift+E", L"mapped", L"nppCommand", L"IDM_VIEW_SWITCHTO_FILEBROWSER", L"Focus file browser panel."}, + {true, L"Display", L"", L"Show Search", L"workbench.view.search", L"Ctrl+Shift+F", L"mapped", L"nppCommand", L"IDM_SEARCH_FINDINFILES", L"Find in files."}, + {true, L"Display", L"", L"Show Source Control", L"workbench.view.scm", L"Ctrl+Shift+G", L"reserved-noop", L"reservedNoOp", L"", L"No source-control sidebar equivalent."}, + {true, L"Display", L"", L"Show Run", L"workbench.view.debug", L"Ctrl+Shift+D", L"reserved-noop", L"reservedNoOp", L"", L"No Run/Debug sidebar equivalent."}, + {true, L"Display", L"", L"Show Extensions", L"workbench.view.extensions", L"Ctrl+Shift+X", L"reserved-noop", L"reservedNoOp", L"", L"No extensions marketplace sidebar equivalent."}, + {true, L"Display", L"", L"Show Output", L"workbench.action.output.toggleOutput", L"Ctrl+Shift+U", L"reserved-noop", L"reservedNoOp", L"", L"No Output panel equivalent."}, + {true, L"Display", L"", L"Quick Open View", L"workbench.action.quickOpenView", L"Ctrl+Q", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code quick-open-view shortcut."}, + {true, L"Display", L"", L"Open New Command Prompt", L"workbench.action.terminal.openNativeConsole", L"Ctrl+Shift+C", L"mapped", L"nppCommand", L"IDM_FILE_OPEN_CMD", L"Approximate with Open Command Prompt Here."}, + {true, L"Display", L"", L"Toggle Markdown Preview", L"markdown.showPreview", L"Ctrl+Shift+V", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code markdown-preview shortcut."}, + {true, L"Display", L"", L"Open Preview to the Side", L"markdown.showPreviewToSide", L"Ctrl+K V", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code markdown-preview chord."}, + {true, L"Display", L"", L"Toggle Integrated Terminal", L"workbench.action.terminal.toggleTerminal", L"Ctrl+`", L"mapped", L"nppCommand", L"IDM_FILE_OPEN_CMD", L"Approximate integrated terminal with Open Command Prompt Here."}, + {true, L"Search", L"", L"Replace in Files", L"workbench.action.replaceInFiles", L"Ctrl+Shift+H", L"mapped", L"nppCommand", L"IDM_SEARCH_FINDINFILES", L"Approximate replace-in-files with Notepad++ find-in-files panel."}, + {false, L"Search", L"", L"Toggle Match Case", L"toggleSearchCaseSensitive", L"Alt+C", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search", L"", L"Toggle Match Whole Word", L"toggleSearchWholeWord", L"Alt+W", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search", L"", L"Toggle Use Regular Expression", L"toggleSearchRegex", L"Alt+R", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Search", L"", L"Toggle Search Details", L"workbench.action.search.toggleQueryDetails", L"Ctrl+Shift+J", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code search-details shortcut."}, + {false, L"Search", L"", L"Focus Next Search Result", L"search.action.focusNextSearchResult", L"F4", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search", L"", L"Focus Previous Search Result", L"search.action.focusPreviousSearchResult", L"Shift+F4", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search", L"", L"Show Next Search Term", L"history.showNext", L"Down", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search", L"", L"Show Previous Search Term", L"history.showPrevious", L"Up", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search Editor", L"", L"Open Results In Editor", L"search.action.openInEditor", L"Alt+Enter", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search Editor", L"", L"Focus Search Editor Input", L"search.action.focusQueryEditorWidget", L"Escape", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search Editor", L"", L"Search Again", L"rerunSearchEditorSearch", L"Ctrl+Shift+R", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Search Editor", L"", L"Delete File Results", L"search.searchEditor.action.deleteFileResults", L"Ctrl+Shift+Backspace", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Preferences", L"", L"Open Settings", L"workbench.action.openSettings", L"Ctrl+,", L"mapped", L"nppCommand", L"IDM_SETTING_PREFERENCE", L"Open Notepad++ preferences."}, + {true, L"Preferences", L"", L"Open Keyboard Shortcuts", L"workbench.action.openGlobalKeybindings", L"Ctrl+K Ctrl+S", L"mapped", L"nppCommand", L"IDM_SETTING_SHORTCUT_MAPPER", L"Open Shortcut Mapper."}, + {true, L"Preferences", L"", L"Select Color Theme", L"workbench.action.selectTheme", L"Ctrl+K Ctrl+T", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code theme chord."}, + {false, L"Chat", L"", L"Open Chat view", L"workbench.action.chat.open", L"Ctrl+Alt+I", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Chat", L"", L"Open chat in agent mode", L"workbench.action.chat.openagent", L"Ctrl+Shift+I", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Chat", L"", L"Open editor inline chat", L"inlineChat.start", L"Ctrl+I", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Chat", L"", L"Open terminal inline chat", L"workbench.action.terminal.chat.start", L"Ctrl+I", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Chat", L"", L"Open quick chat", L"workbench.action.quickchat.toggle", L"Ctrl+Shift+Alt+L", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Chat", L"", L"Open agent picker", L"workbench.action.chat.openModePicker", L"Ctrl+.", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Chat", L"", L"Open language model picker", L"workbench.action.chat.openModelPicker", L"Ctrl+Alt+.", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Chat", L"", L"New chat session", L"workbench.action.chat.newChat", L"Ctrl+N", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Chat", L"", L"Accept inline suggestion", L"editor.action.inlineSuggest.commit", L"Tab", L"documented-unported", L"documentedUnported", L"", L""}, + {true, L"Debug", L"", L"Toggle Breakpoint", L"editor.debug.action.toggleBreakpoint", L"F9", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code breakpoint shortcut."}, + {true, L"Debug", L"", L"Start", L"workbench.action.debug.start", L"F5", L"reserved-noop", L"reservedNoOp", L"", L"Reserved so conflicting Notepad++ behavior does not fire for VS Code debug-start shortcut."}, + {false, L"Debug", L"", L"Continue", L"workbench.action.debug.continue", L"F5", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Debug", L"", L"Start (without debugging)", L"workbench.action.debug.run", L"Ctrl+F5", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Debug", L"", L"Pause", L"workbench.action.debug.pause", L"F6", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Debug", L"", L"Step Into", L"workbench.action.debug.stepInto", L"F11", L"documented-unported", L"documentedUnported", L"", L""}, + {false, L"Tasks", L"", L"Run Build Task", L"workbench.action.tasks.build", L"Ctrl+Shift+B", L"documented-unported", L"documentedUnported", L"", L""}, +}; + const std::vector kShortcutBindings = { - {{true, false, false, 'X'}, ActionKind::CutAllowLine, 0}, // Ctrl+X -> editor.action.clipboardCutAction - {{true, false, false, 'C'}, ActionKind::CopyAllowLine, 0}, // Ctrl+C -> editor.action.clipboardCopyAction - {{true, false, true, 'K'}, ActionKind::SciCommand, SCI_LINEDELETE}, // Ctrl+Shift+K -> editor.action.deleteLines - {{true, false, false, VK_RETURN}, ActionKind::InsertLineBelow, 0}, // Ctrl+Enter -> editor.action.insertLineAfter - {{true, false, true, VK_RETURN}, ActionKind::InsertLineAbove, 0}, // Ctrl+Shift+Enter -> editor.action.insertLineBefore - {{false, true, false, VK_DOWN}, ActionKind::NppCommand, IDM_EDIT_LINE_DOWN}, // Alt+Down -> editor.action.moveLinesDownAction - {{false, true, false, VK_UP}, ActionKind::NppCommand, IDM_EDIT_LINE_UP}, // Alt+Up -> editor.action.moveLinesUpAction - {{false, true, true, VK_DOWN}, ActionKind::NppCommand, IDM_EDIT_DUP_LINE}, // Shift+Alt+Down -> editor.action.copyLinesDownAction - {{false, true, true, VK_UP}, ActionKind::DuplicateLineUp, 0}, // Shift+Alt+Up -> editor.action.copyLinesUpAction - {{true, false, false, 'D'}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTNEXT}, // Ctrl+D -> editor.action.addSelectionToNextFindMatch - {{true, false, false, 'U'}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTUNDO}, // Ctrl+U -> cursorUndo - {{false, true, true, 'I'}, ActionKind::ReservedNoOp, 0}, // Shift+Alt+I -> editor.action.insertCursorAtEndOfEachLineSelected - {{true, false, true, 'L'}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTALL}, // Ctrl+Shift+L -> editor.action.selectHighlights - {{true, false, false, VK_F2}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTALLWHOLEWORD}, // Ctrl+F2 -> editor.action.changeAll - {{true, false, false, 'L'}, ActionKind::SelectCurrentLine, 0}, // Ctrl+L -> expandLineSelection - {{true, true, false, VK_DOWN}, ActionKind::ReservedNoOp, 0}, // Ctrl+Alt+Down -> editor.action.insertCursorBelow - {{true, true, false, VK_UP}, ActionKind::ReservedNoOp, 0}, // Ctrl+Alt+Up -> editor.action.insertCursorAbove - {{true, false, true, VK_OEM_5}, ActionKind::NppCommand, IDM_SEARCH_GOTOMATCHINGBRACE}, // Ctrl+Shift+\ -> editor.action.jumpToBracket - {{true, false, false, VK_OEM_6}, ActionKind::NppCommand, IDM_EDIT_INS_TAB}, // Ctrl+] -> editor.action.indentLines - {{true, false, false, VK_OEM_4}, ActionKind::NppCommand, IDM_EDIT_RMV_TAB}, // Ctrl+[ -> editor.action.outdentLines - {{true, false, false, VK_END}, ActionKind::SciCommand, SCI_DOCUMENTEND}, // Ctrl+End -> cursorBottom - {{true, false, false, VK_HOME}, ActionKind::SciCommand, SCI_DOCUMENTSTART}, // Ctrl+Home -> cursorTop - {{true, false, false, VK_DOWN}, ActionKind::SciCommand, SCI_LINESCROLLDOWN}, // Ctrl+Down -> scrollLineDown - {{true, false, false, VK_UP}, ActionKind::SciCommand, SCI_LINESCROLLUP}, // Ctrl+Up -> scrollLineUp - {{false, true, false, VK_NEXT}, ActionKind::ScrollPageDownNoCaret, 0}, // Alt+PageDown -> scrollPageDown - {{false, true, false, VK_PRIOR}, ActionKind::ScrollPageUpNoCaret, 0}, // Alt+PageUp -> scrollPageUp - {{true, false, true, VK_OEM_4}, ActionKind::NppCommand, IDM_VIEW_FOLD_CURRENT}, // Ctrl+Shift+[ -> editor.fold - {{true, false, true, VK_OEM_6}, ActionKind::NppCommand, IDM_VIEW_UNFOLD_CURRENT}, // Ctrl+Shift+] -> editor.unfold - {{true, false, false, VK_OEM_2}, ActionKind::ToggleStreamComment, 0}, // Ctrl+/ -> editor.action.commentLine - {{false, true, true, 'A'}, ActionKind::NppCommand, IDM_EDIT_BLOCK_COMMENT}, // Shift+Alt+A -> editor.action.blockComment - {{false, true, false, VK_RETURN}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTALL}, // Alt+Enter -> editor.action.selectAllMatches - {{false, true, false, 'Z'}, ActionKind::NppCommand, IDM_VIEW_WRAP}, // Alt+Z -> editor.action.toggleWordWrap - {{true, false, false, VK_SPACE}, ActionKind::NppCommand, IDM_EDIT_AUTOCOMPLETE}, // Ctrl+Space -> editor.action.triggerSuggest - {{true, false, true, VK_SPACE}, ActionKind::NppCommand, IDM_EDIT_FUNCCALLTIP}, // Ctrl+Shift+Space -> editor.action.triggerParameterHints - {{false, true, true, 'F'}, ActionKind::ReservedNoOp, 0}, // Shift+Alt+F -> editor.action.formatDocument - {{false, false, false, VK_F12}, ActionKind::ReservedNoOp, 0}, // F12 -> editor.action.revealDefinition - {{false, true, false, VK_F12}, ActionKind::ReservedNoOp, 0}, // Alt+F12 -> editor.action.peekDefinition - {{true, false, false, VK_OEM_PERIOD}, ActionKind::ReservedNoOp, 0}, // Ctrl+. -> editor.action.quickFix - {{false, false, true, VK_F12}, ActionKind::ReservedNoOp, 0}, // Shift+F12 -> editor.action.goToReferences - {{false, false, false, VK_F2}, ActionKind::ReservedNoOp, 0}, // F2 -> editor.action.rename - {{true, false, false, 'T'}, ActionKind::NppCommand, IDM_VIEW_SWITCHTO_FUNC_LIST}, // Ctrl+T -> workbench.action.showAllSymbols - {{true, false, false, 'G'}, ActionKind::NppCommand, IDM_SEARCH_GOTOLINE}, // Ctrl+G -> workbench.action.gotoLine - {{true, false, false, 'P'}, ActionKind::NppCommand, IDM_FILE_OPEN}, // Ctrl+P -> workbench.action.quickOpen - {{true, false, true, 'O'}, ActionKind::NppCommand, IDM_VIEW_SWITCHTO_FUNC_LIST}, // Ctrl+Shift+O -> workbench.action.gotoSymbol - {{true, false, true, 'M'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Shift+M -> workbench.actions.view.problems - {{false, false, false, VK_F8}, ActionKind::ReservedNoOp, 0}, // F8 -> editor.action.marker.nextInFiles - {{false, false, true, VK_F8}, ActionKind::ReservedNoOp, 0}, // Shift+F8 -> editor.action.marker.prevInFiles - {{true, false, true, 'P'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Shift+P -> workbench.action.showCommands - {{true, false, false, VK_TAB}, ActionKind::NppCommand, IDM_VIEW_TAB_NEXT}, // Ctrl+Tab -> workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup - {{false, true, false, VK_LEFT}, ActionKind::ReservedNoOp, 0}, // Alt+Left -> workbench.action.navigateBack - {{false, true, false, VK_RIGHT}, ActionKind::ReservedNoOp, 0}, // Alt+Right -> workbench.action.navigateForward - {{true, false, true, 'N'}, ActionKind::NppCommand, IDM_VIEW_GOTO_NEW_INSTANCE}, // Ctrl+Shift+N -> workbench.action.newWindow - {{true, false, false, VK_F4}, ActionKind::NppCommand, IDM_FILE_CLOSE}, // Ctrl+F4 -> workbench.action.closeActiveEditor - {{true, false, false, VK_OEM_5}, ActionKind::NppCommand, IDM_VIEW_GOTO_ANOTHER_VIEW}, // Ctrl+\ -> workbench.action.splitEditor - {{true, false, false, '1'}, ActionKind::NppCommand, IDM_VIEW_TAB1}, // Ctrl+1 -> workbench.action.focusFirstEditorGroup - {{true, false, false, '2'}, ActionKind::NppCommand, IDM_VIEW_TAB2}, // Ctrl+2 -> workbench.action.focusSecondEditorGroup - {{true, false, false, '3'}, ActionKind::NppCommand, IDM_VIEW_TAB3}, // Ctrl+3 -> workbench.action.focusThirdEditorGroup - {{true, false, true, VK_PRIOR}, ActionKind::NppCommand, IDM_VIEW_TAB_MOVEBACKWARD}, // Ctrl+Shift+PageUp -> workbench.action.moveEditorLeftInGroup - {{true, false, true, VK_NEXT}, ActionKind::NppCommand, IDM_VIEW_TAB_MOVEFORWARD}, // Ctrl+Shift+PageDown -> workbench.action.moveEditorRightInGroup - {{true, true, false, VK_RIGHT}, ActionKind::NppCommand, IDM_VIEW_GOTO_ANOTHER_VIEW}, // Ctrl+Alt+Right -> workbench.action.moveEditorToNextGroup - {{true, true, false, VK_LEFT}, ActionKind::NppCommand, IDM_VIEW_GOTO_ANOTHER_VIEW}, // Ctrl+Alt+Left -> workbench.action.moveEditorToPreviousGroup - {{true, false, true, 'S'}, ActionKind::NppCommand, IDM_FILE_SAVEAS}, // Ctrl+Shift+S -> workbench.action.files.saveAs - {{true, false, true, 'T'}, ActionKind::NppCommand, IDM_FILE_RESTORELASTCLOSEDFILE}, // Ctrl+Shift+T -> workbench.action.reopenClosedEditor - {{false, false, false, VK_F11}, ActionKind::NppCommand, IDM_VIEW_FULLSCREENTOGGLE}, // F11 -> workbench.action.toggleFullScreen - {{true, false, false, VK_OEM_PLUS}, ActionKind::NppCommand, IDM_VIEW_ZOOMIN}, // Ctrl+= -> workbench.action.zoomIn - {{true, false, false, VK_OEM_MINUS}, ActionKind::NppCommand, IDM_VIEW_ZOOMOUT}, // Ctrl+- -> workbench.action.zoomOut - {{true, false, false, VK_NUMPAD0}, ActionKind::NppCommand, IDM_VIEW_ZOOMRESTORE}, // Ctrl+Numpad0 -> workbench.action.zoomReset - {{true, false, false, 'B'}, ActionKind::NppCommand, IDM_VIEW_FILEBROWSER}, // Ctrl+B -> workbench.action.toggleSidebarVisibility - {{true, false, true, 'E'}, ActionKind::NppCommand, IDM_VIEW_SWITCHTO_FILEBROWSER}, // Ctrl+Shift+E -> workbench.view.explorer - {{true, false, true, 'F'}, ActionKind::NppCommand, IDM_SEARCH_FINDINFILES}, // Ctrl+Shift+F -> workbench.view.search - {{true, false, true, 'G'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Shift+G -> workbench.view.scm - {{true, false, true, 'D'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Shift+D -> workbench.view.debug - {{true, false, true, 'X'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Shift+X -> workbench.view.extensions - {{true, false, true, 'U'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Shift+U -> workbench.action.output.toggleOutput - {{true, false, false, 'Q'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Q -> workbench.action.quickOpenView - {{true, false, true, 'C'}, ActionKind::NppCommand, IDM_FILE_OPEN_CMD}, // Ctrl+Shift+C -> workbench.action.terminal.openNativeConsole - {{true, false, true, 'V'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Shift+V -> markdown.showPreview - {{true, false, false, VK_OEM_3}, ActionKind::NppCommand, IDM_FILE_OPEN_CMD}, // Ctrl+` -> workbench.action.terminal.toggleTerminal - {{true, false, true, 'H'}, ActionKind::NppCommand, IDM_SEARCH_FINDINFILES}, // Ctrl+Shift+H -> workbench.action.replaceInFiles - {{true, false, true, 'J'}, ActionKind::ReservedNoOp, 0}, // Ctrl+Shift+J -> workbench.action.search.toggleQueryDetails - {{true, false, false, VK_OEM_COMMA}, ActionKind::NppCommand, IDM_SETTING_PREFERENCE}, // Ctrl+, -> workbench.action.openSettings - {{false, false, false, VK_F9}, ActionKind::ReservedNoOp, 0}, // F9 -> editor.debug.action.toggleBreakpoint - {{false, false, false, VK_F5}, ActionKind::ReservedNoOp, 0}, // F5 -> workbench.action.debug.start + {{true, false, false, 'X'}, ActionKind::CutAllowLine, 0, 0}, // Ctrl+X -> editor.action.clipboardCutAction + {{true, false, false, 'C'}, ActionKind::CopyAllowLine, 0, 1}, // Ctrl+C -> editor.action.clipboardCopyAction + {{true, false, true, 'K'}, ActionKind::SciCommand, SCI_LINEDELETE, 3}, // Ctrl+Shift+K -> editor.action.deleteLines + {{true, false, false, VK_RETURN}, ActionKind::InsertLineBelow, 0, 4}, // Ctrl+Enter -> editor.action.insertLineAfter + {{true, false, true, VK_RETURN}, ActionKind::InsertLineAbove, 0, 5}, // Ctrl+Shift+Enter -> editor.action.insertLineBefore + {{false, true, false, VK_DOWN}, ActionKind::NppCommand, IDM_EDIT_LINE_DOWN, 6}, // Alt+Down -> editor.action.moveLinesDownAction + {{false, true, false, VK_UP}, ActionKind::NppCommand, IDM_EDIT_LINE_UP, 7}, // Alt+Up -> editor.action.moveLinesUpAction + {{false, true, true, VK_DOWN}, ActionKind::DuplicateLineDown, 0, 8}, // Shift+Alt+Down -> editor.action.copyLinesDownAction + {{false, true, true, VK_UP}, ActionKind::DuplicateLineUp, 0, 9}, // Shift+Alt+Up -> editor.action.copyLinesUpAction + {{true, false, false, 'D'}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTNEXT, 12}, // Ctrl+D -> editor.action.addSelectionToNextFindMatch + {{true, false, false, 'U'}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTUNDO, 14}, // Ctrl+U -> cursorUndo + {{false, true, true, 'I'}, ActionKind::ReservedNoOp, 0, 15}, // Shift+Alt+I -> editor.action.insertCursorAtEndOfEachLineSelected + {{true, false, true, 'L'}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTALL, 16}, // Ctrl+Shift+L -> editor.action.selectHighlights + {{true, false, false, VK_F2}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTALLWHOLEWORD, 17}, // Ctrl+F2 -> editor.action.changeAll + {{true, false, false, 'L'}, ActionKind::SelectCurrentLine, 0, 18}, // Ctrl+L -> expandLineSelection + {{true, true, false, VK_DOWN}, ActionKind::InsertCursorBelow, 0, 19}, // Ctrl+Alt+Down -> editor.action.insertCursorBelow + {{true, true, false, VK_UP}, ActionKind::InsertCursorAbove, 0, 20}, // Ctrl+Alt+Up -> editor.action.insertCursorAbove + {{true, false, true, VK_OEM_5}, ActionKind::NppCommand, IDM_SEARCH_GOTOMATCHINGBRACE, 21}, // Ctrl+Shift+\ -> editor.action.jumpToBracket + {{true, false, false, VK_OEM_6}, ActionKind::NppCommand, IDM_EDIT_INS_TAB, 22}, // Ctrl+] -> editor.action.indentLines + {{true, false, false, VK_OEM_4}, ActionKind::NppCommand, IDM_EDIT_RMV_TAB, 23}, // Ctrl+[ -> editor.action.outdentLines + {{true, false, false, VK_END}, ActionKind::SciCommand, SCI_DOCUMENTEND, 26}, // Ctrl+End -> cursorBottom + {{true, false, false, VK_HOME}, ActionKind::SciCommand, SCI_DOCUMENTSTART, 27}, // Ctrl+Home -> cursorTop + {{true, false, false, VK_DOWN}, ActionKind::SciCommand, SCI_LINESCROLLDOWN, 28}, // Ctrl+Down -> scrollLineDown + {{true, false, false, VK_UP}, ActionKind::SciCommand, SCI_LINESCROLLUP, 29}, // Ctrl+Up -> scrollLineUp + {{false, true, false, VK_NEXT}, ActionKind::ScrollPageDownNoCaret, 0, 30}, // Alt+PageDown -> scrollPageDown + {{false, true, false, VK_PRIOR}, ActionKind::ScrollPageUpNoCaret, 0, 31}, // Alt+PageUp -> scrollPageUp + {{true, false, true, VK_OEM_4}, ActionKind::NppCommand, IDM_VIEW_FOLD_CURRENT, 32}, // Ctrl+Shift+[ -> editor.fold + {{true, false, true, VK_OEM_6}, ActionKind::NppCommand, IDM_VIEW_UNFOLD_CURRENT, 33}, // Ctrl+Shift+] -> editor.unfold + {{true, false, false, VK_OEM_2}, ActionKind::ToggleStreamComment, 0, 41}, // Ctrl+/ -> editor.action.commentLine + {{false, true, true, 'A'}, ActionKind::NppCommand, IDM_EDIT_BLOCK_COMMENT, 42}, // Shift+Alt+A -> editor.action.blockComment + {{false, true, false, VK_RETURN}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTALL, 47}, // Alt+Enter -> editor.action.selectAllMatches + {{false, true, false, 'Z'}, ActionKind::NppCommand, IDM_VIEW_WRAP, 52}, // Alt+Z -> editor.action.toggleWordWrap + {{true, true, true, VK_DOWN}, ActionKind::CursorColumnSelectDown, 0, 53}, // Ctrl+Alt+Shift+Down -> editor.action.cursorColumnSelectDown + {{true, true, true, VK_UP}, ActionKind::CursorColumnSelectUp, 0, 54}, // Ctrl+Alt+Shift+Up -> editor.action.cursorColumnSelectUp + {{true, false, false, VK_SPACE}, ActionKind::NppCommand, IDM_EDIT_AUTOCOMPLETE, 55}, // Ctrl+Space -> editor.action.triggerSuggest + {{true, false, true, VK_SPACE}, ActionKind::NppCommand, IDM_EDIT_FUNCCALLTIP, 56}, // Ctrl+Shift+Space -> editor.action.triggerParameterHints + {{false, true, true, 'F'}, ActionKind::ReservedNoOp, 0, 57}, // Shift+Alt+F -> editor.action.formatDocument + {{false, false, false, VK_F12}, ActionKind::ReservedNoOp, 0, 59}, // F12 -> editor.action.revealDefinition + {{false, true, false, VK_F12}, ActionKind::ReservedNoOp, 0, 61}, // Alt+F12 -> editor.action.peekDefinition + {{true, false, false, VK_OEM_PERIOD}, ActionKind::ReservedNoOp, 0, 63}, // Ctrl+. -> editor.action.quickFix + {{false, false, true, VK_F12}, ActionKind::ReservedNoOp, 0, 64}, // Shift+F12 -> editor.action.goToReferences + {{false, false, false, VK_F2}, ActionKind::ReservedNoOp, 0, 65}, // F2 -> editor.action.rename + {{true, false, false, 'T'}, ActionKind::NppCommand, IDM_VIEW_SWITCHTO_FUNC_LIST, 72}, // Ctrl+T -> workbench.action.showAllSymbols + {{true, false, false, 'G'}, ActionKind::NppCommand, IDM_SEARCH_GOTOLINE, 73}, // Ctrl+G -> workbench.action.gotoLine + {{true, false, false, 'P'}, ActionKind::NppCommand, IDM_FILE_OPEN, 74}, // Ctrl+P -> workbench.action.quickOpen + {{true, false, true, 'O'}, ActionKind::NppCommand, IDM_VIEW_SWITCHTO_FUNC_LIST, 75}, // Ctrl+Shift+O -> workbench.action.gotoSymbol + {{true, false, true, 'M'}, ActionKind::ReservedNoOp, 0, 76}, // Ctrl+Shift+M -> workbench.actions.view.problems + {{false, false, false, VK_F8}, ActionKind::ReservedNoOp, 0, 77}, // F8 -> editor.action.marker.nextInFiles + {{false, false, true, VK_F8}, ActionKind::ReservedNoOp, 0, 78}, // Shift+F8 -> editor.action.marker.prevInFiles + {{true, false, true, 'P'}, ActionKind::ReservedNoOp, 0, 79}, // Ctrl+Shift+P -> workbench.action.showCommands + {{true, false, false, VK_TAB}, ActionKind::NppCommand, IDM_VIEW_TAB_NEXT, 80}, // Ctrl+Tab -> workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup + {{false, true, false, VK_LEFT}, ActionKind::ReservedNoOp, 0, 81}, // Alt+Left -> workbench.action.navigateBack + {{false, true, false, VK_RIGHT}, ActionKind::ReservedNoOp, 0, 83}, // Alt+Right -> workbench.action.navigateForward + {{true, false, true, 'N'}, ActionKind::NppCommand, IDM_VIEW_GOTO_NEW_INSTANCE, 86}, // Ctrl+Shift+N -> workbench.action.newWindow + {{true, false, false, VK_F4}, ActionKind::NppCommand, IDM_FILE_CLOSE, 88}, // Ctrl+F4 -> workbench.action.closeActiveEditor + {{true, false, false, VK_OEM_5}, ActionKind::NppCommand, IDM_VIEW_GOTO_ANOTHER_VIEW, 90}, // Ctrl+\ -> workbench.action.splitEditor + {{true, false, false, '1'}, ActionKind::NppCommand, IDM_VIEW_TAB1, 91}, // Ctrl+1 -> workbench.action.focusFirstEditorGroup + {{true, false, false, '2'}, ActionKind::NppCommand, IDM_VIEW_TAB2, 92}, // Ctrl+2 -> workbench.action.focusSecondEditorGroup + {{true, false, false, '3'}, ActionKind::NppCommand, IDM_VIEW_TAB3, 93}, // Ctrl+3 -> workbench.action.focusThirdEditorGroup + {{true, false, true, VK_PRIOR}, ActionKind::NppCommand, IDM_VIEW_TAB_MOVEBACKWARD, 96}, // Ctrl+Shift+PageUp -> workbench.action.moveEditorLeftInGroup + {{true, false, true, VK_NEXT}, ActionKind::NppCommand, IDM_VIEW_TAB_MOVEFORWARD, 97}, // Ctrl+Shift+PageDown -> workbench.action.moveEditorRightInGroup + {{true, true, false, VK_RIGHT}, ActionKind::NppCommand, IDM_VIEW_GOTO_ANOTHER_VIEW, 100}, // Ctrl+Alt+Right -> workbench.action.moveEditorToNextGroup + {{true, true, false, VK_LEFT}, ActionKind::NppCommand, IDM_VIEW_GOTO_ANOTHER_VIEW, 101}, // Ctrl+Alt+Left -> workbench.action.moveEditorToPreviousGroup + {{true, false, true, 'S'}, ActionKind::NppCommand, IDM_FILE_SAVEAS, 106}, // Ctrl+Shift+S -> workbench.action.files.saveAs + {{true, false, true, 'T'}, ActionKind::NppCommand, IDM_FILE_RESTORELASTCLOSEDFILE, 109}, // Ctrl+Shift+T -> workbench.action.reopenClosedEditor + {{false, false, false, VK_F11}, ActionKind::NppCommand, IDM_VIEW_FULLSCREENTOGGLE, 113}, // F11 -> workbench.action.toggleFullScreen + {{true, false, false, VK_OEM_PLUS}, ActionKind::NppCommand, IDM_VIEW_ZOOMIN, 116}, // Ctrl+= -> workbench.action.zoomIn + {{true, false, false, VK_OEM_MINUS}, ActionKind::NppCommand, IDM_VIEW_ZOOMOUT, 117}, // Ctrl+- -> workbench.action.zoomOut + {{true, false, false, VK_NUMPAD0}, ActionKind::NppCommand, IDM_VIEW_ZOOMRESTORE, 118}, // Ctrl+Numpad0 -> workbench.action.zoomReset + {{true, false, false, 'B'}, ActionKind::NppCommand, IDM_VIEW_FILEBROWSER, 119}, // Ctrl+B -> workbench.action.toggleSidebarVisibility + {{true, false, true, 'E'}, ActionKind::NppCommand, IDM_VIEW_SWITCHTO_FILEBROWSER, 120}, // Ctrl+Shift+E -> workbench.view.explorer + {{true, false, true, 'F'}, ActionKind::NppCommand, IDM_SEARCH_FINDINFILES, 121}, // Ctrl+Shift+F -> workbench.view.search + {{true, false, true, 'G'}, ActionKind::ReservedNoOp, 0, 122}, // Ctrl+Shift+G -> workbench.view.scm + {{true, false, true, 'D'}, ActionKind::ReservedNoOp, 0, 123}, // Ctrl+Shift+D -> workbench.view.debug + {{true, false, true, 'X'}, ActionKind::ReservedNoOp, 0, 124}, // Ctrl+Shift+X -> workbench.view.extensions + {{true, false, true, 'U'}, ActionKind::ReservedNoOp, 0, 125}, // Ctrl+Shift+U -> workbench.action.output.toggleOutput + {{true, false, false, 'Q'}, ActionKind::ReservedNoOp, 0, 126}, // Ctrl+Q -> workbench.action.quickOpenView + {{true, false, true, 'C'}, ActionKind::NppCommand, IDM_FILE_OPEN_CMD, 127}, // Ctrl+Shift+C -> workbench.action.terminal.openNativeConsole + {{true, false, true, 'V'}, ActionKind::ReservedNoOp, 0, 128}, // Ctrl+Shift+V -> markdown.showPreview + {{true, false, false, VK_OEM_3}, ActionKind::NppCommand, IDM_FILE_OPEN_CMD, 130}, // Ctrl+` -> workbench.action.terminal.toggleTerminal + {{true, false, true, 'H'}, ActionKind::NppCommand, IDM_SEARCH_FINDINFILES, 131}, // Ctrl+Shift+H -> workbench.action.replaceInFiles + {{true, false, true, 'J'}, ActionKind::ReservedNoOp, 0, 135}, // Ctrl+Shift+J -> workbench.action.search.toggleQueryDetails + {{true, false, false, VK_OEM_COMMA}, ActionKind::NppCommand, IDM_SETTING_PREFERENCE, 144}, // Ctrl+, -> workbench.action.openSettings + {{false, false, false, VK_F9}, ActionKind::ReservedNoOp, 0, 156}, // F9 -> editor.debug.action.toggleBreakpoint + {{false, false, false, VK_F5}, ActionKind::ReservedNoOp, 0, 157}, // F5 -> workbench.action.debug.start }; const std::vector kChordBindings = { - {{true, false, false, 'K'}, {true, false, false, 'D'}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTSSKIP}, // Ctrl+K Ctrl+D -> editor.action.moveSelectionToNextFindMatch - {{true, false, false, 'K'}, {true, false, false, 'L'}, ActionKind::ToggleFoldAtCaret, 0}, // Ctrl+K Ctrl+L -> editor.toggleFold - {{true, false, false, 'K'}, {true, false, false, VK_OEM_4}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Ctrl+[ -> editor.foldRecursively - {{true, false, false, 'K'}, {true, false, false, VK_OEM_6}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Ctrl+] -> editor.unfoldRecursively - {{true, false, false, 'K'}, {true, false, false, '0'}, ActionKind::NppCommand, IDM_VIEW_FOLDALL}, // Ctrl+K Ctrl+0 -> editor.foldAll - {{true, false, false, 'K'}, {true, false, false, 'J'}, ActionKind::NppCommand, IDM_VIEW_UNFOLDALL}, // Ctrl+K Ctrl+J -> editor.unfoldAll - {{true, false, false, 'K'}, {true, false, false, 'C'}, ActionKind::NppCommand, IDM_EDIT_STREAM_COMMENT}, // Ctrl+K Ctrl+C -> editor.action.addCommentLine - {{true, false, false, 'K'}, {true, false, false, 'U'}, ActionKind::NppCommand, IDM_EDIT_STREAM_UNCOMMENT}, // Ctrl+K Ctrl+U -> editor.action.removeCommentLine - {{true, false, false, 'K'}, {true, false, false, 'F'}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Ctrl+F -> editor.action.formatSelection - {{true, false, false, 'K'}, {true, false, false, 'I'}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Ctrl+I -> editor.action.showHover - {{true, false, false, 'K'}, {false, false, false, VK_F12}, ActionKind::ReservedNoOp, 0}, // Ctrl+K F12 -> editor.action.revealDefinitionAside - {{true, false, false, 'K'}, {true, false, false, 'X'}, ActionKind::NppCommand, IDM_EDIT_TRIMTRAILING}, // Ctrl+K Ctrl+X -> editor.action.trimTrailingWhitespace - {{true, false, false, 'K'}, {false, false, false, 'M'}, ActionKind::ReservedNoOp, 0}, // Ctrl+K M -> workbench.action.editor.changeLanguageMode - {{true, false, false, 'K'}, {false, false, false, 'F'}, ActionKind::ReservedNoOp, 0}, // Ctrl+K F -> workbench.action.closeFolder - {{true, false, false, 'K'}, {true, false, false, VK_LEFT}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Ctrl+Left -> workbench.action.focusLeftGroup - {{true, false, false, 'K'}, {true, false, false, VK_RIGHT}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Ctrl+Right -> workbench.action.focusRightGroup - {{true, false, false, 'K'}, {false, false, false, VK_LEFT}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Left -> workbench.action.moveActiveEditorGroupLeft - {{true, false, false, 'K'}, {false, false, false, VK_RIGHT}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Right -> workbench.action.moveActiveEditorGroupRight - {{true, false, false, 'K'}, {false, false, false, 'S'}, ActionKind::NppCommand, IDM_FILE_SAVEALL}, // Ctrl+K S -> saveAll - {{true, false, false, 'K'}, {false, false, false, 'W'}, ActionKind::ReservedNoOp, 0}, // Ctrl+K W -> workbench.action.closeEditorsInGroup - {{true, false, false, 'K'}, {true, false, false, 'W'}, ActionKind::NppCommand, IDM_FILE_CLOSEALL}, // Ctrl+K Ctrl+W -> workbench.action.closeAllEditors - {{true, false, false, 'K'}, {false, false, false, VK_RETURN}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Enter -> workbench.action.keepEditor - {{true, false, false, 'K'}, {false, false, false, 'P'}, ActionKind::NppCommand, IDM_EDIT_FULLPATHTOCLIP}, // Ctrl+K P -> workbench.action.files.copyPathOfActiveFile - {{true, false, false, 'K'}, {false, false, false, 'R'}, ActionKind::NppCommand, IDM_EDIT_OPENINFOLDER}, // Ctrl+K R -> workbench.action.files.revealActiveFileInWindows - {{true, false, false, 'K'}, {false, false, false, 'Z'}, ActionKind::NppCommand, IDM_VIEW_DISTRACTIONFREE}, // Ctrl+K Z -> workbench.action.toggleZenMode - {{true, false, false, 'K'}, {false, false, false, 'V'}, ActionKind::ReservedNoOp, 0}, // Ctrl+K V -> markdown.showPreviewToSide - {{true, false, false, 'K'}, {true, false, false, 'S'}, ActionKind::NppCommand, IDM_SETTING_SHORTCUT_MAPPER}, // Ctrl+K Ctrl+S -> workbench.action.openGlobalKeybindings - {{true, false, false, 'K'}, {true, false, false, 'T'}, ActionKind::ReservedNoOp, 0}, // Ctrl+K Ctrl+T -> workbench.action.selectTheme + {{true, false, false, 'K'}, {true, false, false, 'D'}, ActionKind::NppCommand, IDM_EDIT_MULTISELECTSSKIP, 13}, // Ctrl+K Ctrl+D -> editor.action.moveSelectionToNextFindMatch + {{true, false, false, 'K'}, {true, false, false, 'L'}, ActionKind::ToggleFoldAtCaret, 0, 34}, // Ctrl+K Ctrl+L -> editor.toggleFold + {{true, false, false, 'K'}, {true, false, false, VK_OEM_4}, ActionKind::ReservedNoOp, 0, 35}, // Ctrl+K Ctrl+[ -> editor.foldRecursively + {{true, false, false, 'K'}, {true, false, false, VK_OEM_6}, ActionKind::ReservedNoOp, 0, 36}, // Ctrl+K Ctrl+] -> editor.unfoldRecursively + {{true, false, false, 'K'}, {true, false, false, '0'}, ActionKind::NppCommand, IDM_VIEW_FOLDALL, 37}, // Ctrl+K Ctrl+0 -> editor.foldAll + {{true, false, false, 'K'}, {true, false, false, 'J'}, ActionKind::NppCommand, IDM_VIEW_UNFOLDALL, 38}, // Ctrl+K Ctrl+J -> editor.unfoldAll + {{true, false, false, 'K'}, {true, false, false, 'C'}, ActionKind::NppCommand, IDM_EDIT_STREAM_COMMENT, 39}, // Ctrl+K Ctrl+C -> editor.action.addCommentLine + {{true, false, false, 'K'}, {true, false, false, 'U'}, ActionKind::NppCommand, IDM_EDIT_STREAM_UNCOMMENT, 40}, // Ctrl+K Ctrl+U -> editor.action.removeCommentLine + {{true, false, false, 'K'}, {true, false, false, 'F'}, ActionKind::ReservedNoOp, 0, 58}, // Ctrl+K Ctrl+F -> editor.action.formatSelection + {{true, false, false, 'K'}, {true, false, false, 'I'}, ActionKind::ReservedNoOp, 0, 60}, // Ctrl+K Ctrl+I -> editor.action.showHover + {{true, false, false, 'K'}, {false, false, false, VK_F12}, ActionKind::ReservedNoOp, 0, 62}, // Ctrl+K F12 -> editor.action.revealDefinitionAside + {{true, false, false, 'K'}, {true, false, false, 'X'}, ActionKind::NppCommand, IDM_EDIT_TRIMTRAILING, 70}, // Ctrl+K Ctrl+X -> editor.action.trimTrailingWhitespace + {{true, false, false, 'K'}, {false, false, false, 'M'}, ActionKind::ReservedNoOp, 0, 71}, // Ctrl+K M -> workbench.action.editor.changeLanguageMode + {{true, false, false, 'K'}, {false, false, false, 'F'}, ActionKind::ReservedNoOp, 0, 89}, // Ctrl+K F -> workbench.action.closeFolder + {{true, false, false, 'K'}, {true, false, false, VK_LEFT}, ActionKind::ReservedNoOp, 0, 94}, // Ctrl+K Ctrl+Left -> workbench.action.focusLeftGroup + {{true, false, false, 'K'}, {true, false, false, VK_RIGHT}, ActionKind::ReservedNoOp, 0, 95}, // Ctrl+K Ctrl+Right -> workbench.action.focusRightGroup + {{true, false, false, 'K'}, {false, false, false, VK_LEFT}, ActionKind::ReservedNoOp, 0, 98}, // Ctrl+K Left -> workbench.action.moveActiveEditorGroupLeft + {{true, false, false, 'K'}, {false, false, false, VK_RIGHT}, ActionKind::ReservedNoOp, 0, 99}, // Ctrl+K Right -> workbench.action.moveActiveEditorGroupRight + {{true, false, false, 'K'}, {false, false, false, 'S'}, ActionKind::NppCommand, IDM_FILE_SAVEALL, 105}, // Ctrl+K S -> saveAll + {{true, false, false, 'K'}, {false, false, false, 'W'}, ActionKind::ReservedNoOp, 0, 107}, // Ctrl+K W -> workbench.action.closeEditorsInGroup + {{true, false, false, 'K'}, {true, false, false, 'W'}, ActionKind::NppCommand, IDM_FILE_CLOSEALL, 108}, // Ctrl+K Ctrl+W -> workbench.action.closeAllEditors + {{true, false, false, 'K'}, {false, false, false, VK_RETURN}, ActionKind::ReservedNoOp, 0, 110}, // Ctrl+K Enter -> workbench.action.keepEditor + {{true, false, false, 'K'}, {false, false, false, 'P'}, ActionKind::NppCommand, IDM_EDIT_FULLPATHTOCLIP, 111}, // Ctrl+K P -> workbench.action.files.copyPathOfActiveFile + {{true, false, false, 'K'}, {false, false, false, 'R'}, ActionKind::NppCommand, IDM_EDIT_OPENINFOLDER, 112}, // Ctrl+K R -> workbench.action.files.revealActiveFileInWindows + {{true, false, false, 'K'}, {false, false, false, 'Z'}, ActionKind::NppCommand, IDM_VIEW_DISTRACTIONFREE, 114}, // Ctrl+K Z -> workbench.action.toggleZenMode + {{true, false, false, 'K'}, {false, false, false, 'V'}, ActionKind::ReservedNoOp, 0, 129}, // Ctrl+K V -> markdown.showPreviewToSide + {{true, false, false, 'K'}, {true, false, false, 'S'}, ActionKind::NppCommand, IDM_SETTING_SHORTCUT_MAPPER, 145}, // Ctrl+K Ctrl+S -> workbench.action.openGlobalKeybindings + {{true, false, false, 'K'}, {true, false, false, 'T'}, ActionKind::ReservedNoOp, 0, 146}, // Ctrl+K Ctrl+T -> workbench.action.selectTheme }; diff --git a/src/VSCodeKeymapNpp.rc.in b/src/VSCodeKeymapNpp.rc.in index 988f3a0..38d3514 100644 --- a/src/VSCodeKeymapNpp.rc.in +++ b/src/VSCodeKeymapNpp.rc.in @@ -1,5 +1,11 @@ #include +#define IDD_CONFIG_REFERENCE 101 +#define IDC_KEYMAP_ENABLED 1001 +#define IDC_REFERENCE_FILTER 1002 +#define IDC_BINDINGS_LIST 1003 +#define IDC_REFERENCE_SUMMARY 1004 + VS_VERSION_INFO VERSIONINFO FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 @@ -27,3 +33,22 @@ BEGIN VALUE "Translation", 0x0409, 1200 END END + +IDD_CONFIG_REFERENCE DIALOGEX 0, 0, 760, 420 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "VSCode Keymap NPP - Config / Reference" +FONT 9, "Segoe UI" +BEGIN + GROUPBOX "Plugin Mode", IDC_STATIC, 8, 4, 250, 22 + CONTROL "Enable VSCode key handling (unchecked = pass through)", IDC_KEYMAP_ENABLED, "Button", + BS_AUTOCHECKBOX | WS_TABSTOP, 18, 13, 225, 10 + GROUPBOX "Binding Search", IDC_STATIC, 8, 28, 280, 26 + LTEXT "Filter shortcuts", IDC_STATIC, 18, 40, 58, 8 + EDITTEXT IDC_REFERENCE_FILTER, 82, 37, 196, 14, + ES_AUTOHSCROLL | WS_TABSTOP + GROUPBOX "Status", IDC_STATIC, 296, 4, 456, 50 + LTEXT "", IDC_REFERENCE_SUMMARY, 306, 17, 436, 28 + CONTROL "", IDC_BINDINGS_LIST, "SysListView32", + LVS_REPORT | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP, 10, 60, 740, 340 + DEFPUSHBUTTON "Close", IDOK, 695, 402, 55, 14 +END diff --git a/src/VSCodeKeymapPlugin.cpp b/src/VSCodeKeymapPlugin.cpp index 312ba88..7755406 100644 --- a/src/VSCodeKeymapPlugin.cpp +++ b/src/VSCodeKeymapPlugin.cpp @@ -1,511 +1,771 @@ -#include - -#include -#include -#include -#include - -#include "PluginInterface.h" -#include "menuCmdID.h" - -namespace { -constexpr wchar_t kPluginName[] = L"VSCode Keymap NPP"; -constexpr DWORD kChordTimeoutMs = 1500; - -NppData g_nppData{}; -FuncItem g_funcItems[2]{}; -bool g_enabled = true; - -WNDPROC g_mainOldProc = nullptr; -WNDPROC g_scintillaOldProc[2] = {nullptr, nullptr}; -HHOOK g_messageHook = nullptr; -DWORD g_nppThreadId = 0; - -struct KeyStroke { - bool ctrl; - bool alt; - bool shift; - UINT vk; -}; - -bool operator==(const KeyStroke& lhs, const KeyStroke& rhs) { - return lhs.ctrl == rhs.ctrl && lhs.alt == rhs.alt && lhs.shift == rhs.shift && lhs.vk == rhs.vk; -} - -enum class ActionKind { - NppCommand, - SciCommand, - DuplicateLineUp, - CutAllowLine, - CopyAllowLine, - ToggleFoldAtCaret, - ToggleStreamComment, - SelectCurrentLine, - InsertLineBelow, - InsertLineAbove, - ScrollPageUpNoCaret, - ScrollPageDownNoCaret, - ReservedNoOp, -}; - -struct ShortcutBinding { - KeyStroke key; - ActionKind kind; - int id; -}; - -struct ChordBinding { - KeyStroke first; - KeyStroke second; - ActionKind kind; - int id; -}; - -struct PendingChord { - KeyStroke first; - DWORD startedAt; -}; - -std::optional g_pendingChord; - -HWND ActiveScintilla() { - int which = 0; - ::SendMessage(g_nppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, reinterpret_cast(&which)); - return (which == 0) ? g_nppData._scintillaMainHandle : g_nppData._scintillaSecondHandle; -} - -int ScintillaIndex(HWND hWnd) { - if (hWnd == g_nppData._scintillaMainHandle) { - return 0; - } - if (hWnd == g_nppData._scintillaSecondHandle) { - return 1; - } - return -1; -} - -bool IsPressed(int vkey) { - return (::GetKeyState(vkey) & 0x8000) != 0; -} - -void RunNppCommand(int commandId) { - ::SendMessage(g_nppData._nppHandle, NPPM_MENUCOMMAND, 0, static_cast(commandId)); -} - -sptr_t RunSci(HWND editor, UINT message, uptr_t wParam = 0, sptr_t lParam = 0) { - return static_cast( - ::SendMessage(editor, message, static_cast(wParam), static_cast(lParam))); -} - -KeyStroke CurrentKeyStroke(UINT vk) { - return KeyStroke{IsPressed(VK_CONTROL), IsPressed(VK_MENU), IsPressed(VK_SHIFT), vk}; -} - -std::string GetLineText(HWND editor, sptr_t line) { - const sptr_t lengthWithEol = RunSci(editor, SCI_GETLINE, static_cast(line), 0); - if (lengthWithEol <= 0) { - return {}; - } - - std::string buffer(static_cast(lengthWithEol), '\0'); - RunSci(editor, SCI_GETLINE, static_cast(line), reinterpret_cast(buffer.data())); - while (!buffer.empty() && (buffer.back() == '\0' || buffer.back() == '\r' || buffer.back() == '\n')) { - buffer.pop_back(); - } - return buffer; -} - -std::string_view TrimLeftAscii(std::string_view text) { - size_t i = 0; - while (i < text.size()) { - const char ch = text[i]; - if (ch != ' ' && ch != '\t') { - break; - } - ++i; - } - return text.substr(i); -} - -bool LooksLineCommented(std::string_view trimmed) { - static constexpr const char* kPrefixes[] = {"//", "#", "--", ";", "'", "REM ", "::"}; - for (const char* prefix : kPrefixes) { - const size_t prefixLen = std::char_traits::length(prefix); - if (trimmed.size() >= prefixLen && trimmed.substr(0, prefixLen) == prefix) { - return true; - } - } - return false; -} - -bool ShouldUncommentSelection(HWND editor) { - const sptr_t selectionStart = RunSci(editor, SCI_GETSELECTIONSTART); - const sptr_t selectionEnd = RunSci(editor, SCI_GETSELECTIONEND); - - sptr_t startLine = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(selectionStart)); - sptr_t endLine = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(selectionEnd)); - if (selectionEnd > selectionStart) { - const sptr_t endLineStart = RunSci(editor, SCI_POSITIONFROMLINE, static_cast(endLine)); - if (selectionEnd == endLineStart && endLine > startLine) { - --endLine; - } - } - - bool sawNonEmptyLine = false; - for (sptr_t line = startLine; line <= endLine; ++line) { - const std::string text = GetLineText(editor, line); - const std::string_view trimmed = TrimLeftAscii(text); - if (trimmed.empty()) { - continue; - } - sawNonEmptyLine = true; - if (!LooksLineCommented(trimmed)) { - return false; - } - } - - return sawNonEmptyLine; -} - -void ConfigureEditor(HWND editor) { - if (editor == nullptr) { - return; - } - - RunSci(editor, SCI_SETMULTIPLESELECTION, 1); - RunSci(editor, SCI_SETADDITIONALSELECTIONTYPING, 1); - RunSci(editor, SCI_SETADDITIONALCARETSVISIBLE, 1); - RunSci(editor, SCI_SETRECTANGULARSELECTIONMODIFIER, SCMOD_ALT); -} - -void ConfigureEditors() { - ConfigureEditor(g_nppData._scintillaMainHandle); - ConfigureEditor(g_nppData._scintillaSecondHandle); -} - -#include "GeneratedBindings.inc" - -bool ExecuteAction(ActionKind kind, int id, HWND editor) { - switch (kind) { - case ActionKind::NppCommand: - RunNppCommand(id); - return true; - - case ActionKind::SciCommand: - RunSci(editor, static_cast(id)); - return true; - - case ActionKind::DuplicateLineUp: - RunNppCommand(IDM_EDIT_DUP_LINE); - RunNppCommand(IDM_EDIT_LINE_UP); - return true; - - case ActionKind::CutAllowLine: { - const bool selectionEmpty = RunSci(editor, SCI_GETSELECTIONEMPTY) != 0; - RunSci(editor, selectionEmpty ? SCI_CUTALLOWLINE : SCI_CUT); - return true; - } - - case ActionKind::CopyAllowLine: { - const bool selectionEmpty = RunSci(editor, SCI_GETSELECTIONEMPTY) != 0; - RunSci(editor, selectionEmpty ? SCI_COPYALLOWLINE : SCI_COPY); - return true; - } - - case ActionKind::ToggleFoldAtCaret: { - sptr_t line = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(RunSci(editor, SCI_GETCURRENTPOS))); - sptr_t level = RunSci(editor, SCI_GETFOLDLEVEL, static_cast(line)); - if ((level & SC_FOLDLEVELHEADERFLAG) == 0) { - const sptr_t parent = RunSci(editor, SCI_GETFOLDPARENT, static_cast(line)); - if (parent >= 0) { - line = parent; - } - } - RunSci(editor, SCI_TOGGLEFOLD, static_cast(line)); - return true; - } - - case ActionKind::ToggleStreamComment: - RunNppCommand(ShouldUncommentSelection(editor) ? IDM_EDIT_STREAM_UNCOMMENT : IDM_EDIT_STREAM_COMMENT); - return true; - - case ActionKind::SelectCurrentLine: { - const sptr_t currentPos = RunSci(editor, SCI_GETCURRENTPOS); - const sptr_t line = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(currentPos)); - const sptr_t lineStart = RunSci(editor, SCI_POSITIONFROMLINE, static_cast(line)); - const sptr_t lineEnd = RunSci(editor, SCI_GETLINEENDPOSITION, static_cast(line)); - RunSci(editor, SCI_SETSEL, static_cast(lineStart), lineEnd); - return true; - } - - case ActionKind::InsertLineBelow: { - const sptr_t currentPos = RunSci(editor, SCI_GETCURRENTPOS); - const sptr_t line = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(currentPos)); - const sptr_t lineEnd = RunSci(editor, SCI_GETLINEENDPOSITION, static_cast(line)); - RunSci(editor, SCI_SETSEL, static_cast(lineEnd), lineEnd); - RunSci(editor, SCI_NEWLINE); - return true; - } - - case ActionKind::InsertLineAbove: { - const sptr_t currentPos = RunSci(editor, SCI_GETCURRENTPOS); - const sptr_t line = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(currentPos)); - const sptr_t lineStart = RunSci(editor, SCI_POSITIONFROMLINE, static_cast(line)); - RunSci(editor, SCI_SETSEL, static_cast(lineStart), lineStart); - RunSci(editor, SCI_NEWLINE); - RunSci(editor, SCI_LINEUP); - RunSci(editor, SCI_HOME); - return true; - } - - case ActionKind::ScrollPageUpNoCaret: { - sptr_t lines = RunSci(editor, SCI_LINESONSCREEN); - if (lines < 1) { - lines = 30; - } - RunSci(editor, SCI_LINESCROLL, 0, -lines); - return true; - } - - case ActionKind::ScrollPageDownNoCaret: { - sptr_t lines = RunSci(editor, SCI_LINESONSCREEN); - if (lines < 1) { - lines = 30; - } - RunSci(editor, SCI_LINESCROLL, 0, lines); - return true; - } - - case ActionKind::ReservedNoOp: - return true; - } - - return false; -} - -bool TryHandleChord(const KeyStroke& currentKey, HWND editor) { - if (g_pendingChord.has_value()) { - const DWORD age = ::GetTickCount() - g_pendingChord->startedAt; - if (age <= kChordTimeoutMs) { - for (const auto& chord : kChordBindings) { - if (chord.first == g_pendingChord->first && chord.second == currentKey) { - g_pendingChord.reset(); - return ExecuteAction(chord.kind, chord.id, editor); - } - } - - // Strict mode: consume unknown second key after a valid chord prefix. - g_pendingChord.reset(); - return true; - } - - g_pendingChord.reset(); - } - - for (const auto& chord : kChordBindings) { - if (chord.first == currentKey) { - g_pendingChord = PendingChord{currentKey, ::GetTickCount()}; - return true; - } - } - - return false; -} - -bool TryHandleShortcut(HWND editor, WPARAM wParam) { - if (!g_enabled || editor == nullptr) { - return false; - } - - const UINT vk = static_cast(wParam); - if (vk == VK_CONTROL || vk == VK_MENU || vk == VK_SHIFT) { - return false; - } - - const KeyStroke currentKey = CurrentKeyStroke(vk); - - if (TryHandleChord(currentKey, editor)) { - return true; - } - - for (const auto& binding : kShortcutBindings) { - if (binding.key == currentKey) { - return ExecuteAction(binding.kind, binding.id, editor); - } - } - - return false; -} - -LRESULT CALLBACK MessageHookProc(int code, WPARAM wParam, LPARAM lParam) { - if (code >= 0 && wParam == PM_REMOVE) { - MSG* msg = reinterpret_cast(lParam); - if (msg != nullptr && (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)) { - if (TryHandleShortcut(ActiveScintilla(), msg->wParam)) { - msg->message = WM_NULL; - msg->wParam = 0; - msg->lParam = 0; - return 1; - } - } - } - - return ::CallNextHookEx(g_messageHook, code, wParam, lParam); -} - -LRESULT CALLBACK ScintillaProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if ((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && TryHandleShortcut(hWnd, wParam)) { - return 0; - } - - const int idx = ScintillaIndex(hWnd); - if (idx >= 0 && g_scintillaOldProc[idx] != nullptr) { - return ::CallWindowProc(g_scintillaOldProc[idx], hWnd, msg, wParam, lParam); - } - - return ::DefWindowProc(hWnd, msg, wParam, lParam); -} - -LRESULT CALLBACK MainProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if ((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && TryHandleShortcut(ActiveScintilla(), wParam)) { - return 0; - } - - if (g_mainOldProc != nullptr) { - return ::CallWindowProc(g_mainOldProc, hWnd, msg, wParam, lParam); - } - - return ::DefWindowProc(hWnd, msg, wParam, lParam); -} - -void HookWindows() { - if (g_nppData._nppHandle != nullptr && g_mainOldProc == nullptr) { - g_mainOldProc = reinterpret_cast( - ::SetWindowLongPtr(g_nppData._nppHandle, GWLP_WNDPROC, reinterpret_cast(MainProc))); - } - - if (g_nppData._scintillaMainHandle != nullptr && g_scintillaOldProc[0] == nullptr) { - g_scintillaOldProc[0] = reinterpret_cast(::SetWindowLongPtr( - g_nppData._scintillaMainHandle, GWLP_WNDPROC, reinterpret_cast(ScintillaProc))); - } - - if (g_nppData._scintillaSecondHandle != nullptr && g_scintillaOldProc[1] == nullptr) { - g_scintillaOldProc[1] = reinterpret_cast(::SetWindowLongPtr( - g_nppData._scintillaSecondHandle, GWLP_WNDPROC, reinterpret_cast(ScintillaProc))); - } -} - -void UnhookWindows() { - if (g_nppData._nppHandle != nullptr && g_mainOldProc != nullptr) { - ::SetWindowLongPtr(g_nppData._nppHandle, GWLP_WNDPROC, reinterpret_cast(g_mainOldProc)); - g_mainOldProc = nullptr; - } - - if (g_nppData._scintillaMainHandle != nullptr && g_scintillaOldProc[0] != nullptr) { - ::SetWindowLongPtr(g_nppData._scintillaMainHandle, GWLP_WNDPROC, - reinterpret_cast(g_scintillaOldProc[0])); - g_scintillaOldProc[0] = nullptr; - } - - if (g_nppData._scintillaSecondHandle != nullptr && g_scintillaOldProc[1] != nullptr) { - ::SetWindowLongPtr(g_nppData._scintillaSecondHandle, GWLP_WNDPROC, - reinterpret_cast(g_scintillaOldProc[1])); - g_scintillaOldProc[1] = nullptr; - } -} - -void InstallMessageHook() { - if (g_messageHook != nullptr || g_nppData._nppHandle == nullptr) { - return; - } - - g_nppThreadId = ::GetWindowThreadProcessId(g_nppData._nppHandle, nullptr); - if (g_nppThreadId == 0) { - return; - } - - g_messageHook = ::SetWindowsHookEx(WH_GETMESSAGE, MessageHookProc, nullptr, g_nppThreadId); -} - -void RemoveMessageHook() { - if (g_messageHook != nullptr) { - ::UnhookWindowsHookEx(g_messageHook); - g_messageHook = nullptr; - } - - g_nppThreadId = 0; - g_pendingChord.reset(); -} - -void ToggleKeymap() { - g_enabled = !g_enabled; - g_pendingChord.reset(); - - const wchar_t* status = g_enabled ? L"enabled" : L"disabled"; - std::wstring message = L"VSCode strict keymap is now "; - message += status; - message += L"."; - ::MessageBox(g_nppData._nppHandle, message.c_str(), kPluginName, MB_OK | MB_ICONINFORMATION); -} - -void ShowBindingsSummary() { - std::wstring summary = L"VSCode Keymap NPP strict mode\n\n"; - summary += L"Source bindings: "; - summary += std::to_wstring(kVsCodeSourceBindingCount); - summary += L"\nMapped: "; - summary += std::to_wstring(kMappedBindingCount); - summary += L"\nReserved no-op: "; - summary += std::to_wstring(kReservedNoOpBindingCount); - summary += L"\nDocumented unported: "; - summary += std::to_wstring(kDocumentedUnportedBindingCount); - summary += L"\n\nCoverage report installs under:\n"; - summary += L"plugins\\doc\\VSCodeKeymapNpp\\VSCodeKeybindingCoverage.MD"; - ::MessageBox(g_nppData._nppHandle, summary.c_str(), kPluginName, MB_OK | MB_ICONINFORMATION); -} - -void RegisterPluginCommands() { - std::wmemset(g_funcItems[0]._itemName, 0, menuItemSize); - std::wmemset(g_funcItems[1]._itemName, 0, menuItemSize); - - ::wcsncpy_s(g_funcItems[0]._itemName, menuItemSize, L"Toggle VSCode Strict Keymap", _TRUNCATE); - g_funcItems[0]._pFunc = ToggleKeymap; - - ::wcsncpy_s(g_funcItems[1]._itemName, menuItemSize, L"Show VSCode Coverage Summary", _TRUNCATE); - g_funcItems[1]._pFunc = ShowBindingsSummary; -} - -} // namespace - -extern "C" __declspec(dllexport) void setInfo(NppData notepadPlusData) { - g_nppData = notepadPlusData; - RegisterPluginCommands(); - HookWindows(); - ConfigureEditors(); - InstallMessageHook(); -} - -extern "C" __declspec(dllexport) const wchar_t* getName() { - return kPluginName; -} - -extern "C" __declspec(dllexport) FuncItem* getFuncsArray(int* nbF) { - *nbF = 2; - return g_funcItems; -} - -extern "C" __declspec(dllexport) void beNotified(SCNotification*) { - // Not used for this implementation. -} - -extern "C" __declspec(dllexport) LRESULT messageProc(UINT, WPARAM, LPARAM) { - return TRUE; -} - -extern "C" __declspec(dllexport) BOOL isUnicode() { - return TRUE; -} - -extern "C" __declspec(dllexport) void cleanUp() { - RemoveMessageHook(); - UnhookWindows(); +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Notepad_plus_msgs.h" +#include "PluginInterface.h" +#include "handlers/ActionHandlers.h" + +namespace { +constexpr wchar_t kPluginName[] = L"VSCode Keymap NPP"; +constexpr DWORD kChordTimeoutMs = 1500; +constexpr int kConfigDialogId = 101; +constexpr int kEnableKeymapCheckboxId = 1001; +constexpr int kReferenceFilterEditId = 1002; +constexpr int kBindingsListId = 1003; +constexpr int kReferenceSummaryId = 1004; +constexpr wchar_t kSettingsSectionGeneral[] = L"General"; +constexpr wchar_t kSettingsSectionBindings[] = L"Bindings"; +constexpr wchar_t kSettingsKeyEnabled[] = L"KeymapEnabled"; +constexpr wchar_t kLegacySettingsKeyPluginDisabled[] = L"PluginDisabled"; +constexpr wchar_t kSettingsKeyDisabledBindings[] = L"DisabledBindings"; + +NppData g_nppData{}; +FuncItem g_funcItems[2]{}; +bool g_enabled = true; +bool g_syncingBindingList = false; +size_t g_shortcutHandlingPauseDepth = 0; +std::vector g_bindingEnabled; + +WNDPROC g_mainOldProc = nullptr; +WNDPROC g_scintillaOldProc[2] = {nullptr, nullptr}; +HHOOK g_messageHook = nullptr; +DWORD g_nppThreadId = 0; + +struct PendingChord { + KeyStroke first; + DWORD startedAt; +}; + +std::optional g_pendingChord; + +#include "GeneratedBindings.inc" + +constexpr unsigned long long kBindingStorageHashOffset = 14695981039346656037ull; +constexpr unsigned long long kBindingStorageHashPrime = 1099511628211ull; + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +class ScopedShortcutHandlingPause { +public: + ScopedShortcutHandlingPause() { + ++g_shortcutHandlingPauseDepth; + g_pendingChord.reset(); + } + + ~ScopedShortcutHandlingPause() { + if (g_shortcutHandlingPauseDepth > 0) { + --g_shortcutHandlingPauseDepth; + } + g_pendingChord.reset(); + } + + ScopedShortcutHandlingPause(const ScopedShortcutHandlingPause&) = delete; + ScopedShortcutHandlingPause& operator=(const ScopedShortcutHandlingPause&) = delete; +}; + +HINSTANCE ModuleInstance() { + return reinterpret_cast(&__ImageBase); +} + +void ResetBindingStatesToDefaults() { + g_bindingEnabled.assign(kBindingReferences.size(), false); + for (size_t index = 0; index < kBindingReferences.size(); ++index) { + g_bindingEnabled[index] = kBindingReferences[index].pluginHandled; + } +} + +std::filesystem::path SettingsFilePath() { + if (g_nppData._nppHandle == nullptr) { + return {}; + } + + std::vector buffer(MAX_PATH, L'\0'); + const auto copied = static_cast(::SendMessage(g_nppData._nppHandle, + NPPM_GETPLUGINSCONFIGDIR, + static_cast(buffer.size()), + reinterpret_cast(buffer.data()))); + if (copied == 0 || buffer[0] == L'\0') { + return {}; + } + + return std::filesystem::path(buffer.data()) / L"VSCodeKeymapNpp.ini"; +} + +std::wstring SettingsFilePathText() { + const auto path = SettingsFilePath(); + if (path.empty()) { + return L"(Notepad++ plugin config directory unavailable)"; + } + return path.wstring(); +} + +std::wstring GetWindowTextValue(HWND control) { + const int length = ::GetWindowTextLengthW(control); + if (length <= 0) { + return {}; + } + + std::wstring text(static_cast(length) + 1, L'\0'); + ::GetWindowTextW(control, text.data(), length + 1); + text.resize(static_cast(length)); + return text; +} + +unsigned long long HashBindingStorageComponent(unsigned long long hash, std::wstring_view value) { + for (const wchar_t ch : value) { + hash ^= static_cast(static_cast(ch)); + hash *= kBindingStorageHashPrime; + } + hash ^= 0xffull; + hash *= kBindingStorageHashPrime; + return hash; +} + +unsigned long long BindingStorageHash(const BindingReferenceEntry& binding) { + auto hash = kBindingStorageHashOffset; + hash = HashBindingStorageComponent(hash, binding.command ? binding.command : L""); + hash = HashBindingStorageComponent(hash, binding.winKey ? binding.winKey : L""); + return hash; +} + +std::wstring BuildBindingStorageToken(const BindingReferenceEntry& binding) { + wchar_t token[19]{}; + std::swprintf(token, std::size(token), L"h:%016llx", BindingStorageHash(binding)); + return token; +} + +std::optional FindBindingIndexFromPersistedToken(std::wstring_view token) { + std::wstring tokenText(token); + if (tokenText.empty()) { + return std::nullopt; + } + + if (tokenText.rfind(L"h:", 0) == 0) { + wchar_t* parsedEnd = nullptr; + const auto parsedHash = std::wcstoull(tokenText.c_str() + 2, &parsedEnd, 16); + if (parsedEnd != tokenText.c_str() + 2 && *parsedEnd == L'\0') { + for (size_t index = 0; index < kBindingReferences.size(); ++index) { + if (kBindingReferences[index].pluginHandled && BindingStorageHash(kBindingReferences[index]) == parsedHash) { + return index; + } + } + } + return std::nullopt; + } + + wchar_t* parsedEnd = nullptr; + const auto legacyIndex = std::wcstoul(tokenText.c_str(), &parsedEnd, 10); + if (parsedEnd == tokenText.c_str() || *parsedEnd != L'\0' || legacyIndex >= kBindingReferences.size()) { + return std::nullopt; + } + + return static_cast(legacyIndex); +} + +std::wstring BuildDisabledBindingList() { + std::wstring value; + for (size_t index = 0; index < kBindingReferences.size(); ++index) { + if (!kBindingReferences[index].pluginHandled || g_bindingEnabled[index]) { + continue; + } + if (!value.empty()) { + value += L","; + } + value += BuildBindingStorageToken(kBindingReferences[index]); + } + return value; +} + +void ApplyDisabledBindingList(const std::wstring& value) { + std::wstringstream stream(value); + std::wstring token; + while (std::getline(stream, token, L',')) { + if (token.empty()) { + continue; + } + + const auto index = FindBindingIndexFromPersistedToken(token); + if (!index.has_value()) { + continue; + } + if (kBindingReferences[*index].pluginHandled) { + g_bindingEnabled[*index] = false; + } + } +} + +void SaveSettings() { + const auto settingsPath = SettingsFilePath(); + if (settingsPath.empty()) { + return; + } + + std::filesystem::create_directories(settingsPath.parent_path()); + ::WritePrivateProfileStringW( + kSettingsSectionGeneral, kSettingsKeyEnabled, g_enabled ? L"1" : L"0", settingsPath.c_str()); + ::WritePrivateProfileStringW(kSettingsSectionGeneral, kLegacySettingsKeyPluginDisabled, nullptr, settingsPath.c_str()); + + const auto disabledBindings = BuildDisabledBindingList(); + ::WritePrivateProfileStringW(kSettingsSectionBindings, + kSettingsKeyDisabledBindings, + disabledBindings.empty() ? L"" : disabledBindings.c_str(), + settingsPath.c_str()); +} + +void LoadSettings() { + ResetBindingStatesToDefaults(); + + const auto settingsPath = SettingsFilePath(); + if (settingsPath.empty()) { + return; + } + + g_enabled = ::GetPrivateProfileIntW(kSettingsSectionGeneral, kSettingsKeyEnabled, 1, settingsPath.c_str()) != 0; + const bool legacyPluginDisabled = + ::GetPrivateProfileIntW(kSettingsSectionGeneral, kLegacySettingsKeyPluginDisabled, 0, settingsPath.c_str()) != + 0; + g_enabled = g_enabled && !legacyPluginDisabled; + + std::wstring disabledBindings(4096, L'\0'); + const auto length = ::GetPrivateProfileStringW(kSettingsSectionBindings, + kSettingsKeyDisabledBindings, + L"", + disabledBindings.data(), + static_cast(disabledBindings.size()), + settingsPath.c_str()); + disabledBindings.resize(length); + ApplyDisabledBindingList(disabledBindings); +} + +bool IsBindingConfigurable(size_t referenceIndex) { + return referenceIndex < kBindingReferences.size() && kBindingReferences[referenceIndex].pluginHandled; +} + +bool IsBindingEnabled(size_t referenceIndex) { + return IsBindingConfigurable(referenceIndex) && g_bindingEnabled[referenceIndex]; +} + +void SetBindingEnabled(size_t referenceIndex, bool enabled) { + if (!IsBindingConfigurable(referenceIndex)) { + return; + } + + g_bindingEnabled[referenceIndex] = enabled; + g_pendingChord.reset(); + SaveSettings(); +} + +std::wstring BuildReferenceSummaryText() { + const auto enabledCount = static_cast(std::count(g_bindingEnabled.begin(), g_bindingEnabled.end(), true)); + const auto configurableCount = static_cast( + std::count_if(kBindingReferences.begin(), kBindingReferences.end(), [](const auto& binding) { + return binding.pluginHandled; + })); + + std::wstring text; + if (g_enabled) { + text = L"Mode: intercepting selected VS Code shortcuts"; + } else { + text = L"Mode: pass-through (saved selections are preserved)"; + } + text += L"\r\nChecked rows: "; + text += std::to_wstring(enabledCount); + text += L" / "; + text += std::to_wstring(configurableCount); + text += L" Settings file: "; + text += SettingsFilePathText(); + return text; +} + +std::wstring BuildSectionLabel(const BindingReferenceEntry& entry) { + std::wstring value = entry.section ? entry.section : L""; + if (entry.subsection != nullptr && entry.subsection[0] != L'\0') { + if (!value.empty()) { + value += L" / "; + } + value += entry.subsection; + } + return value; +} + +bool ContainsCaseInsensitive(std::wstring_view text, std::wstring_view needle) { + if (needle.empty()) { + return true; + } + + const auto match = std::search(text.begin(), + text.end(), + needle.begin(), + needle.end(), + [](wchar_t lhs, wchar_t rhs) { return std::towlower(lhs) == std::towlower(rhs); }); + return match != text.end(); +} + +bool BindingMatchesFilter(const BindingReferenceEntry& binding, std::wstring_view filter) { + if (filter.empty()) { + return true; + } + + const auto section = BuildSectionLabel(binding); + return ContainsCaseInsensitive(binding.winKey ? binding.winKey : L"", filter) || + ContainsCaseInsensitive(section, filter) || + ContainsCaseInsensitive(binding.label ? binding.label : L"", filter) || + ContainsCaseInsensitive(binding.command ? binding.command : L"", filter) || + ContainsCaseInsensitive(binding.status ? binding.status : L"", filter) || + ContainsCaseInsensitive(binding.handler ? binding.handler : L"", filter) || + ContainsCaseInsensitive(binding.target ? binding.target : L"", filter) || + ContainsCaseInsensitive(binding.notes ? binding.notes : L"", filter); +} + +void InsertReferenceColumns(HWND listView) { + struct ColumnSpec { + const wchar_t* title; + int width; + }; + + const ColumnSpec columns[] = { + {L"Key", 120}, + {L"Section", 170}, + {L"Label", 170}, + {L"Command", 240}, + {L"Status", 110}, + {L"Handler", 120}, + {L"Target", 180}, + {L"Notes", 420}, + }; + + for (int index = 0; index < static_cast(std::size(columns)); ++index) { + LVCOLUMNW column{}; + column.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + column.cx = columns[index].width; + column.pszText = const_cast(columns[index].title); + column.iSubItem = index; + ListView_InsertColumn(listView, index, &column); + } +} + +size_t PopulateReferenceList(HWND dialog, HWND listView) { + const auto filter = GetWindowTextValue(::GetDlgItem(dialog, kReferenceFilterEditId)); + g_syncingBindingList = true; + ListView_DeleteAllItems(listView); + + int visibleIndex = 0; + for (size_t referenceIndex = 0; referenceIndex < kBindingReferences.size(); ++referenceIndex) { + const auto& binding = kBindingReferences[referenceIndex]; + if (!BindingMatchesFilter(binding, filter)) { + continue; + } + + LVITEMW item{}; + item.mask = LVIF_TEXT | LVIF_PARAM; + item.iItem = visibleIndex; + item.lParam = static_cast(referenceIndex); + item.pszText = const_cast(binding.winKey); + ListView_InsertItem(listView, &item); + + const auto section = BuildSectionLabel(binding); + ListView_SetItemText(listView, visibleIndex, 1, const_cast(section.c_str())); + ListView_SetItemText(listView, visibleIndex, 2, const_cast(binding.label)); + ListView_SetItemText(listView, visibleIndex, 3, const_cast(binding.command)); + ListView_SetItemText(listView, visibleIndex, 4, const_cast(binding.status)); + ListView_SetItemText(listView, visibleIndex, 5, const_cast(binding.handler)); + ListView_SetItemText(listView, visibleIndex, 6, const_cast(binding.target)); + ListView_SetItemText(listView, visibleIndex, 7, const_cast(binding.notes)); + ListView_SetCheckState(listView, visibleIndex, IsBindingEnabled(referenceIndex) ? TRUE : FALSE); + ++visibleIndex; + } + + g_syncingBindingList = false; + return static_cast(visibleIndex); +} + +size_t RefreshReferenceList(HWND dialog) { + const HWND listView = ::GetDlgItem(dialog, kBindingsListId); + const size_t visibleCount = PopulateReferenceList(dialog, listView); + ::SetDlgItemTextW(dialog, kReferenceSummaryId, BuildReferenceSummaryText().c_str()); + return visibleCount; +} + +void ApplyDialogSettings(HWND dialog) { + g_enabled = (::IsDlgButtonChecked(dialog, kEnableKeymapCheckboxId) == BST_CHECKED); + g_pendingChord.reset(); + SaveSettings(); +} + +size_t GetReferenceIndexForListItem(HWND listView, int itemIndex) { + LVITEMW item{}; + item.mask = LVIF_PARAM; + item.iItem = itemIndex; + item.iSubItem = 0; + if (!ListView_GetItem(listView, &item) || item.lParam < 0) { + return kBindingReferences.size(); + } + return static_cast(item.lParam); +} + +INT_PTR CALLBACK ConfigReferenceDialogProc(HWND dialog, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_INITDIALOG: { + const HWND listView = ::GetDlgItem(dialog, kBindingsListId); + ListView_SetExtendedListViewStyle( + listView, LVS_EX_CHECKBOXES | LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); + InsertReferenceColumns(listView); + RefreshReferenceList(dialog); + + ::CheckDlgButton(dialog, kEnableKeymapCheckboxId, g_enabled ? BST_CHECKED : BST_UNCHECKED); + return TRUE; + } + + case WM_NOTIFY: { + auto* header = reinterpret_cast(lParam); + if (header == nullptr || header->idFrom != kBindingsListId || header->code != LVN_ITEMCHANGED) { + break; + } + + auto* changed = reinterpret_cast(lParam); + if (g_syncingBindingList || changed->iItem < 0 || (changed->uChanged & LVIF_STATE) == 0) { + break; + } + + const bool oldChecked = ((changed->uOldState & LVIS_STATEIMAGEMASK) >> 12) == 2; + const bool newChecked = ListView_GetCheckState(header->hwndFrom, changed->iItem) != FALSE; + if (oldChecked == newChecked) { + break; + } + + const auto referenceIndex = GetReferenceIndexForListItem(header->hwndFrom, changed->iItem); + if (referenceIndex >= kBindingReferences.size()) { + return TRUE; + } + + if (!IsBindingConfigurable(referenceIndex)) { + g_syncingBindingList = true; + ListView_SetCheckState(header->hwndFrom, changed->iItem, FALSE); + g_syncingBindingList = false; + return TRUE; + } + + SetBindingEnabled(referenceIndex, newChecked); + ::SetDlgItemTextW(dialog, kReferenceSummaryId, BuildReferenceSummaryText().c_str()); + return TRUE; + } + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case kEnableKeymapCheckboxId: + if (HIWORD(wParam) == BN_CLICKED) { + ApplyDialogSettings(dialog); + ::SetDlgItemTextW(dialog, kReferenceSummaryId, BuildReferenceSummaryText().c_str()); + } + return TRUE; + + case kReferenceFilterEditId: + if (HIWORD(wParam) == EN_CHANGE) { + RefreshReferenceList(dialog); + } + return TRUE; + + case IDOK: + case IDCANCEL: + ::EndDialog(dialog, LOWORD(wParam)); + return TRUE; + } + break; + + case WM_CLOSE: + ::EndDialog(dialog, IDCANCEL); + return TRUE; + } + + return FALSE; +} + +HWND ActiveScintilla() { + int which = 0; + ::SendMessage(g_nppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, reinterpret_cast(&which)); + return (which == 0) ? g_nppData._scintillaMainHandle : g_nppData._scintillaSecondHandle; +} + +int ScintillaIndex(HWND hWnd) { + if (hWnd == g_nppData._scintillaMainHandle) { + return 0; + } + if (hWnd == g_nppData._scintillaSecondHandle) { + return 1; + } + return -1; +} + +bool IsPressed(int vkey) { + return (::GetKeyState(vkey) & 0x8000) != 0; +} + +KeyStroke CurrentKeyStroke(UINT vk) { + return KeyStroke{IsPressed(VK_CONTROL), IsPressed(VK_MENU), IsPressed(VK_SHIFT), vk}; +} + +void ConfigureEditor(HWND editor) { + if (editor == nullptr) { + return; + } + + RunSci(editor, SCI_SETMULTIPLESELECTION, 1); + RunSci(editor, SCI_SETADDITIONALSELECTIONTYPING, 1); + RunSci(editor, SCI_SETADDITIONALCARETSVISIBLE, 1); + RunSci(editor, SCI_SETRECTANGULARSELECTIONMODIFIER, SCMOD_ALT); +} + +void ConfigureEditors() { + ConfigureEditor(g_nppData._scintillaMainHandle); + ConfigureEditor(g_nppData._scintillaSecondHandle); +} + +bool TryHandleChord(const KeyStroke& currentKey, HWND editor) { + if (g_pendingChord.has_value()) { + const DWORD age = ::GetTickCount() - g_pendingChord->startedAt; + if (age <= kChordTimeoutMs) { + bool enabledPrefixMatched = false; + for (const auto& chord : kChordBindings) { + if (chord.first != g_pendingChord->first || !IsBindingEnabled(chord.referenceIndex)) { + continue; + } + + enabledPrefixMatched = true; + if (chord.second == currentKey) { + g_pendingChord.reset(); + return ExecuteAction(chord.kind, chord.id, ActionContext{g_nppData._nppHandle, editor}); + } + } + + if (enabledPrefixMatched) { + // Strict mode: consume unknown second key after a valid chord prefix. + g_pendingChord.reset(); + return true; + } + } + + g_pendingChord.reset(); + } + + for (const auto& chord : kChordBindings) { + if (chord.first == currentKey && IsBindingEnabled(chord.referenceIndex)) { + g_pendingChord = PendingChord{currentKey, ::GetTickCount()}; + return true; + } + } + + return false; +} + +bool TryHandleShortcut(HWND editor, WPARAM wParam) { + if (g_shortcutHandlingPauseDepth > 0 || !g_enabled || editor == nullptr) { + return false; + } + + const UINT vk = static_cast(wParam); + if (vk == VK_CONTROL || vk == VK_MENU || vk == VK_SHIFT) { + return false; + } + + const KeyStroke currentKey = CurrentKeyStroke(vk); + + if (TryHandleChord(currentKey, editor)) { + return true; + } + + for (const auto& binding : kShortcutBindings) { + if (binding.key == currentKey && IsBindingEnabled(binding.referenceIndex)) { + return ExecuteAction(binding.kind, binding.id, ActionContext{g_nppData._nppHandle, editor}); + } + } + + return false; +} + +LRESULT CALLBACK MessageHookProc(int code, WPARAM wParam, LPARAM lParam) { + if (code >= 0 && wParam == PM_REMOVE) { + MSG* msg = reinterpret_cast(lParam); + if (msg != nullptr && (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)) { + if (TryHandleShortcut(ActiveScintilla(), msg->wParam)) { + msg->message = WM_NULL; + msg->wParam = 0; + msg->lParam = 0; + return 1; + } + } + } + + return ::CallNextHookEx(g_messageHook, code, wParam, lParam); +} + +LRESULT CALLBACK ScintillaProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + if ((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && TryHandleShortcut(hWnd, wParam)) { + return 0; + } + + const int idx = ScintillaIndex(hWnd); + if (idx >= 0 && g_scintillaOldProc[idx] != nullptr) { + return ::CallWindowProc(g_scintillaOldProc[idx], hWnd, msg, wParam, lParam); + } + + return ::DefWindowProc(hWnd, msg, wParam, lParam); +} + +LRESULT CALLBACK MainProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + if ((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && TryHandleShortcut(ActiveScintilla(), wParam)) { + return 0; + } + + if (g_mainOldProc != nullptr) { + return ::CallWindowProc(g_mainOldProc, hWnd, msg, wParam, lParam); + } + + return ::DefWindowProc(hWnd, msg, wParam, lParam); +} + +void HookWindows() { + if (g_nppData._nppHandle != nullptr && g_mainOldProc == nullptr) { + g_mainOldProc = reinterpret_cast( + ::SetWindowLongPtr(g_nppData._nppHandle, GWLP_WNDPROC, reinterpret_cast(MainProc))); + } + + if (g_nppData._scintillaMainHandle != nullptr && g_scintillaOldProc[0] == nullptr) { + g_scintillaOldProc[0] = reinterpret_cast(::SetWindowLongPtr( + g_nppData._scintillaMainHandle, GWLP_WNDPROC, reinterpret_cast(ScintillaProc))); + } + + if (g_nppData._scintillaSecondHandle != nullptr && g_scintillaOldProc[1] == nullptr) { + g_scintillaOldProc[1] = reinterpret_cast(::SetWindowLongPtr( + g_nppData._scintillaSecondHandle, GWLP_WNDPROC, reinterpret_cast(ScintillaProc))); + } +} + +void UnhookWindows() { + if (g_nppData._nppHandle != nullptr && g_mainOldProc != nullptr) { + ::SetWindowLongPtr(g_nppData._nppHandle, GWLP_WNDPROC, reinterpret_cast(g_mainOldProc)); + g_mainOldProc = nullptr; + } + + if (g_nppData._scintillaMainHandle != nullptr && g_scintillaOldProc[0] != nullptr) { + ::SetWindowLongPtr(g_nppData._scintillaMainHandle, GWLP_WNDPROC, + reinterpret_cast(g_scintillaOldProc[0])); + g_scintillaOldProc[0] = nullptr; + } + + if (g_nppData._scintillaSecondHandle != nullptr && g_scintillaOldProc[1] != nullptr) { + ::SetWindowLongPtr(g_nppData._scintillaSecondHandle, GWLP_WNDPROC, + reinterpret_cast(g_scintillaOldProc[1])); + g_scintillaOldProc[1] = nullptr; + } +} + +void InstallMessageHook() { + if (g_messageHook != nullptr || g_nppData._nppHandle == nullptr) { + return; + } + + g_nppThreadId = ::GetWindowThreadProcessId(g_nppData._nppHandle, nullptr); + if (g_nppThreadId == 0) { + return; + } + + g_messageHook = ::SetWindowsHookEx(WH_GETMESSAGE, MessageHookProc, nullptr, g_nppThreadId); +} + +void RemoveMessageHook() { + if (g_messageHook != nullptr) { + ::UnhookWindowsHookEx(g_messageHook); + g_messageHook = nullptr; + } + + g_nppThreadId = 0; + g_pendingChord.reset(); +} + +void ShowConfigReferenceDialog() { + const ScopedShortcutHandlingPause pause; + INITCOMMONCONTROLSEX controls{sizeof(controls), ICC_LISTVIEW_CLASSES}; + ::InitCommonControlsEx(&controls); + ::DialogBoxParamW(ModuleInstance(), + MAKEINTRESOURCEW(kConfigDialogId), + g_nppData._nppHandle, + ConfigReferenceDialogProc, + 0); +} + +void ShowAboutDialog() { + const ScopedShortcutHandlingPause pause; + std::wstring summary = L"VSCode Keymap NPP\r\n\r\n"; + summary += L"VSCode Keymap NPP strict mode\r\n\r\n"; + summary += L"- Enforces near 1:1 mappings where Notepad++ equivalents exist\r\n"; + summary += L"- Implements missing line operations (move/duplicate/insert/delete)\r\n"; + summary += L"- Uses a pre-accelerator hook to block conflicting default Notepad++ shortcuts\r\n"; + summary += L"- Unsupported VS Code-only shortcuts are reserved as no-op so they do not trigger non-VS Code Notepad++ actions.\r\n\r\n"; + summary += L"Source bindings: "; + summary += std::to_wstring(kVsCodeSourceBindingCount); + summary += L"\r\nMapped: "; + summary += std::to_wstring(kMappedBindingCount); + summary += L"\r\nReserved no-op: "; + summary += std::to_wstring(kReservedNoOpBindingCount); + summary += L"\r\nDocumented unported: "; + summary += std::to_wstring(kDocumentedUnportedBindingCount); + summary += L"\r\n\r\nUse Config/Reference to review and control handled shortcuts.\r\nRepository:\r\nhttps://github.com/14ag/vscode-to-notepadpp\r\n\r\nSettings file:\r\n"; + summary += SettingsFilePathText(); + ::MessageBoxW(g_nppData._nppHandle, summary.c_str(), L"About VSCode Keymap NPP", MB_OK | MB_ICONINFORMATION); +} + +void RegisterPluginCommands() { + std::wmemset(g_funcItems[0]._itemName, 0, menuItemSize); + std::wmemset(g_funcItems[1]._itemName, 0, menuItemSize); + ::wcsncpy_s(g_funcItems[0]._itemName, menuItemSize, L"Config/Reference", _TRUNCATE); + g_funcItems[0]._pFunc = ShowConfigReferenceDialog; + ::wcsncpy_s(g_funcItems[1]._itemName, menuItemSize, L"About", _TRUNCATE); + g_funcItems[1]._pFunc = ShowAboutDialog; +} + +} // namespace + +extern "C" __declspec(dllexport) void setInfo(NppData notepadPlusData) { + g_nppData = notepadPlusData; + LoadSettings(); + RegisterPluginCommands(); + HookWindows(); + ConfigureEditors(); + InstallMessageHook(); +} + +extern "C" __declspec(dllexport) const wchar_t* getName() { + return kPluginName; +} + +extern "C" __declspec(dllexport) FuncItem* getFuncsArray(int* nbF) { + *nbF = 2; + return g_funcItems; +} + +extern "C" __declspec(dllexport) void beNotified(SCNotification*) { + // Not used for this implementation. +} + +extern "C" __declspec(dllexport) LRESULT messageProc(UINT, WPARAM, LPARAM) { + return TRUE; +} + +extern "C" __declspec(dllexport) BOOL isUnicode() { + return TRUE; +} + +extern "C" __declspec(dllexport) void cleanUp() { + RemoveMessageHook(); + UnhookWindows(); } diff --git a/src/handlers/ActionHandlers.cpp b/src/handlers/ActionHandlers.cpp new file mode 100644 index 0000000..66de92c --- /dev/null +++ b/src/handlers/ActionHandlers.cpp @@ -0,0 +1,75 @@ +#include "ActionHandlers.h" + +#include "CustomHandlers.h" +#include "Notepad_plus_msgs.h" + +void RunNppCommand(HWND nppHandle, int commandId) { + ::SendMessage(nppHandle, NPPM_MENUCOMMAND, 0, static_cast(commandId)); +} + +sptr_t RunSci(HWND editor, UINT message, uptr_t wParam, sptr_t lParam) { + return static_cast( + ::SendMessage(editor, message, static_cast(wParam), static_cast(lParam))); +} + +bool ExecuteAction(ActionKind kind, int id, const ActionContext& context) { + switch (kind) { + case ActionKind::NppCommand: + RunNppCommand(context.nppHandle, id); + return true; + + case ActionKind::SciCommand: + RunSci(context.editor, static_cast(id)); + return true; + + case ActionKind::DuplicateLineDown: + return ExecuteDuplicateLineDown(context); + + case ActionKind::DuplicateLineUp: + return ExecuteDuplicateLineUp(context); + + case ActionKind::CutAllowLine: + return ExecuteCutAllowLine(context); + + case ActionKind::CopyAllowLine: + return ExecuteCopyAllowLine(context); + + case ActionKind::ToggleFoldAtCaret: + return ExecuteToggleFoldAtCaret(context); + + case ActionKind::ToggleStreamComment: + return ExecuteToggleStreamComment(context); + + case ActionKind::SelectCurrentLine: + return ExecuteSelectCurrentLine(context); + + case ActionKind::InsertLineBelow: + return ExecuteInsertLineBelow(context); + + case ActionKind::InsertLineAbove: + return ExecuteInsertLineAbove(context); + + case ActionKind::InsertCursorBelow: + return ExecuteInsertCursorBelow(context); + + case ActionKind::InsertCursorAbove: + return ExecuteInsertCursorAbove(context); + + case ActionKind::CursorColumnSelectDown: + return ExecuteCursorColumnSelectDown(context); + + case ActionKind::CursorColumnSelectUp: + return ExecuteCursorColumnSelectUp(context); + + case ActionKind::ScrollPageUpNoCaret: + return ExecuteScrollPageUpNoCaret(context); + + case ActionKind::ScrollPageDownNoCaret: + return ExecuteScrollPageDownNoCaret(context); + + case ActionKind::ReservedNoOp: + return true; + } + + return false; +} diff --git a/src/handlers/ActionHandlers.h b/src/handlers/ActionHandlers.h new file mode 100644 index 0000000..34e84d8 --- /dev/null +++ b/src/handlers/ActionHandlers.h @@ -0,0 +1,78 @@ +#pragma once + +#include + +#include + +#include "Scintilla.h" +#include "menuCmdID.h" + +struct KeyStroke { + bool ctrl; + bool alt; + bool shift; + UINT vk; +}; + +inline bool operator==(const KeyStroke& lhs, const KeyStroke& rhs) { + return lhs.ctrl == rhs.ctrl && lhs.alt == rhs.alt && lhs.shift == rhs.shift && lhs.vk == rhs.vk; +} + +enum class ActionKind { + NppCommand, + SciCommand, + DuplicateLineDown, + DuplicateLineUp, + CutAllowLine, + CopyAllowLine, + ToggleFoldAtCaret, + ToggleStreamComment, + SelectCurrentLine, + InsertLineBelow, + InsertLineAbove, + InsertCursorBelow, + InsertCursorAbove, + CursorColumnSelectDown, + CursorColumnSelectUp, + ScrollPageUpNoCaret, + ScrollPageDownNoCaret, + ReservedNoOp, +}; + +struct ShortcutBinding { + KeyStroke key; + ActionKind kind; + int id; + size_t referenceIndex; +}; + +struct ChordBinding { + KeyStroke first; + KeyStroke second; + ActionKind kind; + int id; + size_t referenceIndex; +}; + +struct BindingReferenceEntry { + bool pluginHandled; + const wchar_t* section; + const wchar_t* subsection; + const wchar_t* label; + const wchar_t* command; + const wchar_t* winKey; + const wchar_t* status; + const wchar_t* handler; + const wchar_t* target; + const wchar_t* notes; +}; + +struct ActionContext { + HWND nppHandle; + HWND editor; +}; + +void RunNppCommand(HWND nppHandle, int commandId); +sptr_t RunSci(HWND editor, UINT message, uptr_t wParam = 0, sptr_t lParam = 0); + +bool ExecuteAction(ActionKind kind, int id, const ActionContext& context); diff --git a/src/handlers/CommentHandlers.cpp b/src/handlers/CommentHandlers.cpp new file mode 100644 index 0000000..43d43f6 --- /dev/null +++ b/src/handlers/CommentHandlers.cpp @@ -0,0 +1,82 @@ +#include "CustomHandlers.h" + +#include +#include + +#include "menuCmdID.h" + +namespace { + +std::string GetLineText(HWND editor, sptr_t line) { + const sptr_t lengthWithEol = RunSci(editor, SCI_GETLINE, static_cast(line), 0); + if (lengthWithEol <= 0) { + return {}; + } + + std::string buffer(static_cast(lengthWithEol), '\0'); + RunSci(editor, SCI_GETLINE, static_cast(line), reinterpret_cast(buffer.data())); + while (!buffer.empty() && (buffer.back() == '\0' || buffer.back() == '\r' || buffer.back() == '\n')) { + buffer.pop_back(); + } + return buffer; +} + +std::string_view TrimLeftAscii(std::string_view text) { + size_t i = 0; + while (i < text.size()) { + const char ch = text[i]; + if (ch != ' ' && ch != '\t') { + break; + } + ++i; + } + return text.substr(i); +} + +bool LooksLineCommented(std::string_view trimmed) { + static constexpr const char* kPrefixes[] = {"//", "#", "--", ";", "'", "REM ", "::"}; + for (const char* prefix : kPrefixes) { + const size_t prefixLen = std::char_traits::length(prefix); + if (trimmed.size() >= prefixLen && trimmed.substr(0, prefixLen) == prefix) { + return true; + } + } + return false; +} + +bool ShouldUncommentSelection(HWND editor) { + const sptr_t selectionStart = RunSci(editor, SCI_GETSELECTIONSTART); + const sptr_t selectionEnd = RunSci(editor, SCI_GETSELECTIONEND); + + sptr_t startLine = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(selectionStart)); + sptr_t endLine = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(selectionEnd)); + if (selectionEnd > selectionStart) { + const sptr_t endLineStart = RunSci(editor, SCI_POSITIONFROMLINE, static_cast(endLine)); + if (selectionEnd == endLineStart && endLine > startLine) { + --endLine; + } + } + + bool sawNonEmptyLine = false; + for (sptr_t line = startLine; line <= endLine; ++line) { + const std::string text = GetLineText(editor, line); + const std::string_view trimmed = TrimLeftAscii(text); + if (trimmed.empty()) { + continue; + } + sawNonEmptyLine = true; + if (!LooksLineCommented(trimmed)) { + return false; + } + } + + return sawNonEmptyLine; +} + +} // namespace + +bool ExecuteToggleStreamComment(const ActionContext& context) { + RunNppCommand(context.nppHandle, + ShouldUncommentSelection(context.editor) ? IDM_EDIT_STREAM_UNCOMMENT : IDM_EDIT_STREAM_COMMENT); + return true; +} diff --git a/src/handlers/CursorHandlers.cpp b/src/handlers/CursorHandlers.cpp new file mode 100644 index 0000000..8ae82d6 --- /dev/null +++ b/src/handlers/CursorHandlers.cpp @@ -0,0 +1,357 @@ +#include "CustomHandlers.h" + +#include +#include + +namespace { + +struct SelectionEndpoint { + sptr_t position; + sptr_t virtualSpace; +}; + +struct SelectionState { + SelectionEndpoint caret; + SelectionEndpoint anchor; +}; + +struct SelectionColumns { + sptr_t caret; + sptr_t anchor; +}; + +struct VerticalSelectionState { + HWND editor; + SelectionState selection; + SelectionColumns columns; + bool active; +}; + +struct ColumnSelectionState { + VerticalSelectionState anchor; + sptr_t extent; +}; + +ColumnSelectionState g_columnSelection{}; +VerticalSelectionState g_insertCursor{}; +CursorVirtualSpaceOptions g_cursorVirtualSpaceOptions{}; + +class ScopedCaretBlinkPause { +public: + explicit ScopedCaretBlinkPause(HWND editor) + : editor_(editor), + caretPeriod_(RunSci(editor, SCI_GETCARETPERIOD)), + additionalCaretsBlink_(RunSci(editor, SCI_GETADDITIONALCARETSBLINK)) { + RunSci(editor_, SCI_SETCARETPERIOD, 0); + RunSci(editor_, SCI_SETADDITIONALCARETSBLINK, 0); + } + + ~ScopedCaretBlinkPause() { + RunSci(editor_, SCI_SETCARETPERIOD, static_cast(caretPeriod_)); + RunSci(editor_, SCI_SETADDITIONALCARETSBLINK, static_cast(additionalCaretsBlink_)); + } + + ScopedCaretBlinkPause(const ScopedCaretBlinkPause&) = delete; + ScopedCaretBlinkPause& operator=(const ScopedCaretBlinkPause&) = delete; + +private: + HWND editor_; + sptr_t caretPeriod_; + sptr_t additionalCaretsBlink_; +}; + +SelectionState GetSelectionState(HWND editor, sptr_t selectionIndex) { + return SelectionState{ + {RunSci(editor, SCI_GETSELECTIONNCARET, static_cast(selectionIndex)), + RunSci(editor, SCI_GETSELECTIONNCARETVIRTUALSPACE, static_cast(selectionIndex))}, + {RunSci(editor, SCI_GETSELECTIONNANCHOR, static_cast(selectionIndex)), + RunSci(editor, SCI_GETSELECTIONNANCHORVIRTUALSPACE, static_cast(selectionIndex))}, + }; +} + +bool SameSelectionState(const SelectionState& lhs, const SelectionState& rhs) { + return lhs.caret.position == rhs.caret.position && lhs.caret.virtualSpace == rhs.caret.virtualSpace && + lhs.anchor.position == rhs.anchor.position && lhs.anchor.virtualSpace == rhs.anchor.virtualSpace; +} + +sptr_t EndpointColumn(HWND editor, const SelectionEndpoint& endpoint) { + return RunSci(editor, SCI_GETCOLUMN, static_cast(endpoint.position)) + endpoint.virtualSpace; +} + +sptr_t EndpointLine(HWND editor, const SelectionEndpoint& endpoint) { + return RunSci(editor, SCI_LINEFROMPOSITION, static_cast(endpoint.position)); +} + +SelectionColumns GetSelectionColumns(HWND editor, const SelectionState& selection) { + return SelectionColumns{EndpointColumn(editor, selection.caret), EndpointColumn(editor, selection.anchor)}; +} + +std::optional GetMainSelection(HWND editor) { + const sptr_t selectionCount = RunSci(editor, SCI_GETSELECTIONS); + if (selectionCount <= 0) { + return std::nullopt; + } + + const sptr_t mainSelection = RunSci(editor, SCI_GETMAINSELECTION); + if (mainSelection < 0 || mainSelection >= selectionCount) { + return std::nullopt; + } + + return GetSelectionState(editor, mainSelection); +} + +std::optional MoveEndpoint(HWND editor, + const SelectionEndpoint& endpoint, + sptr_t lineDelta, + sptr_t desiredColumn, + bool maintainVirtualSpace) { + const sptr_t currentLine = RunSci(editor, SCI_LINEFROMPOSITION, static_cast(endpoint.position)); + const sptr_t targetLine = currentLine + lineDelta; + const sptr_t lineCount = RunSci(editor, SCI_GETLINECOUNT); + if (targetLine < 0 || targetLine >= lineCount) { + return std::nullopt; + } + + const sptr_t targetPos = + RunSci(editor, SCI_FINDCOLUMN, static_cast(targetLine), desiredColumn); + if (targetPos < 0) { + return std::nullopt; + } + + const sptr_t actualColumn = RunSci(editor, SCI_GETCOLUMN, static_cast(targetPos)); + const sptr_t targetVirtualSpace = + (maintainVirtualSpace && desiredColumn > actualColumn) ? (desiredColumn - actualColumn) : 0; + return SelectionEndpoint{targetPos, targetVirtualSpace}; +} + +std::optional MoveSelection(HWND editor, + const SelectionState& selection, + sptr_t lineDelta, + const SelectionColumns& columns, + bool maintainVirtualSpace) { + const auto movedCaret = MoveEndpoint(editor, selection.caret, lineDelta, columns.caret, maintainVirtualSpace); + if (!movedCaret.has_value()) { + return std::nullopt; + } + + const auto movedAnchor = MoveEndpoint(editor, selection.anchor, lineDelta, columns.anchor, maintainVirtualSpace); + if (!movedAnchor.has_value()) { + return std::nullopt; + } + + return SelectionState{*movedCaret, *movedAnchor}; +} + +sptr_t FindSelectionState(HWND editor, const SelectionState& selection) { + const sptr_t selectionCount = RunSci(editor, SCI_GETSELECTIONS); + for (sptr_t i = 0; i < selectionCount; ++i) { + if (SameSelectionState(GetSelectionState(editor, i), selection)) { + return i; + } + } + return -1; +} + +void SetSelectionState(HWND editor, sptr_t selectionIndex, const SelectionState& selection) { + RunSci(editor, SCI_SETSELECTIONNCARET, static_cast(selectionIndex), selection.caret.position); + RunSci(editor, SCI_SETSELECTIONNANCHOR, static_cast(selectionIndex), selection.anchor.position); + RunSci(editor, SCI_SETSELECTIONNCARETVIRTUALSPACE, static_cast(selectionIndex), + selection.caret.virtualSpace); + RunSci(editor, SCI_SETSELECTIONNANCHORVIRTUALSPACE, static_cast(selectionIndex), + selection.anchor.virtualSpace); +} + +sptr_t AbsExtent(sptr_t value) { + return value < 0 ? -value : value; +} + +std::optional> BuildColumnSelections(HWND editor, + const VerticalSelectionState& anchor, + sptr_t extent) { + const sptr_t firstDelta = extent < 0 ? extent : 0; + const sptr_t lastDelta = extent > 0 ? extent : 0; + std::vector selections; + selections.reserve(static_cast(lastDelta - firstDelta + 1)); + + for (sptr_t delta = firstDelta; delta <= lastDelta; ++delta) { + const auto movedSelection = + MoveSelection(editor, + anchor.selection, + delta, + anchor.columns, + g_cursorVirtualSpaceOptions.maintainVirtualSpace); + if (!movedSelection.has_value()) { + return std::nullopt; + } + selections.push_back(*movedSelection); + } + + return selections; +} + +bool ApplySelectionSet(HWND editor, const std::vector& selections, sptr_t mainSelectionIndex) { + if (selections.empty()) { + return false; + } + + RunSci(editor, SCI_SETSELECTION, static_cast(selections[0].caret.position), + selections[0].anchor.position); + SetSelectionState(editor, 0, selections[0]); + + for (size_t i = 1; i < selections.size(); ++i) { + RunSci(editor, SCI_ADDSELECTION, static_cast(selections[i].caret.position), + selections[i].anchor.position); + SetSelectionState(editor, static_cast(i), selections[i]); + } + + RunSci(editor, SCI_SETMAINSELECTION, static_cast(mainSelectionIndex)); + RunSci(editor, SCI_SCROLLCARET); + return true; +} + +bool ApplyColumnSelection(HWND editor, const VerticalSelectionState& anchor, sptr_t extent) { + const auto selections = BuildColumnSelections(editor, anchor, extent); + if (!selections.has_value()) { + return false; + } + + const sptr_t firstDelta = extent < 0 ? extent : 0; + const sptr_t mainSelectionIndex = extent - firstDelta; + return ApplySelectionSet(editor, *selections, mainSelectionIndex); +} + +void ResetColumnSelection() { + g_columnSelection = ColumnSelectionState{}; +} + +void ResetInsertCursorSelection() { + g_insertCursor = VerticalSelectionState{}; +} + +bool IsColumnSelectionCurrent(HWND editor) { + if (!g_columnSelection.anchor.active || g_columnSelection.anchor.editor != editor) { + return false; + } + + const sptr_t expectedCount = AbsExtent(g_columnSelection.extent) + 1; + if (RunSci(editor, SCI_GETSELECTIONS) != expectedCount) { + return false; + } + + const auto selections = BuildColumnSelections(editor, g_columnSelection.anchor, g_columnSelection.extent); + if (!selections.has_value()) { + return false; + } + + for (const auto& selection : *selections) { + if (FindSelectionState(editor, selection) < 0) { + return false; + } + } + + return true; +} + +bool StartColumnSelection(HWND editor) { + const auto mainSelection = GetMainSelection(editor); + if (!mainSelection.has_value()) { + return false; + } + + g_columnSelection = ColumnSelectionState{ + VerticalSelectionState{editor, *mainSelection, GetSelectionColumns(editor, *mainSelection), true}, 0}; + return true; +} + +bool UpdateColumnSelection(HWND editor, sptr_t direction) { + ScopedCaretBlinkPause caretBlinkPause(editor); + + if (!IsColumnSelectionCurrent(editor) && !StartColumnSelection(editor)) { + return true; + } + + const sptr_t newExtent = g_columnSelection.extent + direction; + if (!ApplyColumnSelection(editor, g_columnSelection.anchor, newExtent)) { + return true; + } + + g_columnSelection.extent = newExtent; + return true; +} + +bool InsertCursorOnAdjacentLine(HWND editor, sptr_t lineDelta) { + ScopedCaretBlinkPause caretBlinkPause(editor); + + const auto currentSelection = GetMainSelection(editor); + if (!currentSelection.has_value()) { + return true; + } + + if (!g_insertCursor.active || g_insertCursor.editor != editor || + !SameSelectionState(g_insertCursor.selection, *currentSelection)) { + g_insertCursor = VerticalSelectionState{ + editor, *currentSelection, GetSelectionColumns(editor, *currentSelection), true}; + } + + const auto movedSelection = MoveSelection( + editor, + *currentSelection, + lineDelta, + g_insertCursor.columns, + g_cursorVirtualSpaceOptions.maintainVirtualSpace); + if (!movedSelection.has_value()) { + return true; + } + + const sptr_t existingSelection = FindSelectionState(editor, *movedSelection); + if (existingSelection >= 0) { + RunSci(editor, SCI_SETMAINSELECTION, static_cast(existingSelection)); + RunSci(editor, SCI_SCROLLCARET); + g_insertCursor.selection = GetSelectionState(editor, existingSelection); + return true; + } + + RunSci(editor, SCI_ADDSELECTION, static_cast(movedSelection->caret.position), + movedSelection->anchor.position); + const sptr_t newSelectionIndex = RunSci(editor, SCI_GETMAINSELECTION); + if (newSelectionIndex < 0) { + return true; + } + SetSelectionState(editor, newSelectionIndex, *movedSelection); + RunSci(editor, SCI_SETMAINSELECTION, static_cast(newSelectionIndex)); + RunSci(editor, SCI_SCROLLCARET); + g_insertCursor.selection = *movedSelection; + return true; +} + +} // namespace + +CursorVirtualSpaceOptions GetCursorVirtualSpaceOptions() { + return g_cursorVirtualSpaceOptions; +} + +void SetCursorVirtualSpaceOptions(const CursorVirtualSpaceOptions& options) { + g_cursorVirtualSpaceOptions = options; + ResetColumnSelection(); + ResetInsertCursorSelection(); +} + +bool ExecuteInsertCursorBelow(const ActionContext& context) { + ResetColumnSelection(); + return InsertCursorOnAdjacentLine(context.editor, 1); +} + +bool ExecuteInsertCursorAbove(const ActionContext& context) { + ResetColumnSelection(); + return InsertCursorOnAdjacentLine(context.editor, -1); +} + +bool ExecuteCursorColumnSelectDown(const ActionContext& context) { + ResetInsertCursorSelection(); + return UpdateColumnSelection(context.editor, 1); +} + +bool ExecuteCursorColumnSelectUp(const ActionContext& context) { + ResetInsertCursorSelection(); + return UpdateColumnSelection(context.editor, -1); +} diff --git a/src/handlers/CustomHandlers.h b/src/handlers/CustomHandlers.h new file mode 100644 index 0000000..80fd622 --- /dev/null +++ b/src/handlers/CustomHandlers.h @@ -0,0 +1,26 @@ +#pragma once + +#include "ActionHandlers.h" + +struct CursorVirtualSpaceOptions { + bool maintainVirtualSpace = false; +}; + +CursorVirtualSpaceOptions GetCursorVirtualSpaceOptions(); +void SetCursorVirtualSpaceOptions(const CursorVirtualSpaceOptions& options); + +bool ExecuteDuplicateLineDown(const ActionContext& context); +bool ExecuteDuplicateLineUp(const ActionContext& context); +bool ExecuteCutAllowLine(const ActionContext& context); +bool ExecuteCopyAllowLine(const ActionContext& context); +bool ExecuteToggleFoldAtCaret(const ActionContext& context); +bool ExecuteToggleStreamComment(const ActionContext& context); +bool ExecuteSelectCurrentLine(const ActionContext& context); +bool ExecuteInsertLineBelow(const ActionContext& context); +bool ExecuteInsertLineAbove(const ActionContext& context); +bool ExecuteInsertCursorBelow(const ActionContext& context); +bool ExecuteInsertCursorAbove(const ActionContext& context); +bool ExecuteCursorColumnSelectDown(const ActionContext& context); +bool ExecuteCursorColumnSelectUp(const ActionContext& context); +bool ExecuteScrollPageUpNoCaret(const ActionContext& context); +bool ExecuteScrollPageDownNoCaret(const ActionContext& context); diff --git a/src/handlers/FoldHandlers.cpp b/src/handlers/FoldHandlers.cpp new file mode 100644 index 0000000..621a7fc --- /dev/null +++ b/src/handlers/FoldHandlers.cpp @@ -0,0 +1,15 @@ +#include "CustomHandlers.h" + +bool ExecuteToggleFoldAtCaret(const ActionContext& context) { + sptr_t line = + RunSci(context.editor, SCI_LINEFROMPOSITION, static_cast(RunSci(context.editor, SCI_GETCURRENTPOS))); + const sptr_t level = RunSci(context.editor, SCI_GETFOLDLEVEL, static_cast(line)); + if ((level & SC_FOLDLEVELHEADERFLAG) == 0) { + const sptr_t parent = RunSci(context.editor, SCI_GETFOLDPARENT, static_cast(line)); + if (parent >= 0) { + line = parent; + } + } + RunSci(context.editor, SCI_TOGGLEFOLD, static_cast(line)); + return true; +} diff --git a/src/handlers/LineHandlers.cpp b/src/handlers/LineHandlers.cpp new file mode 100644 index 0000000..bd36993 --- /dev/null +++ b/src/handlers/LineHandlers.cpp @@ -0,0 +1,85 @@ +#include "CustomHandlers.h" + +#include "menuCmdID.h" + +namespace { + +bool DuplicateLineWithCaretTarget(const ActionContext& context, sptr_t targetLineOffset, bool moveDuplicateUp) { + const bool selectionEmpty = RunSci(context.editor, SCI_GETSELECTIONEMPTY) != 0; + if (!selectionEmpty) { + RunNppCommand(context.nppHandle, IDM_EDIT_DUP_LINE); + if (moveDuplicateUp) { + RunNppCommand(context.nppHandle, IDM_EDIT_LINE_UP); + } + return true; + } + + const sptr_t currentPos = RunSci(context.editor, SCI_GETCURRENTPOS); + const sptr_t currentLine = RunSci(context.editor, SCI_LINEFROMPOSITION, static_cast(currentPos)); + const sptr_t currentColumn = RunSci(context.editor, SCI_GETCOLUMN, static_cast(currentPos)); + + RunNppCommand(context.nppHandle, IDM_EDIT_DUP_LINE); + if (moveDuplicateUp) { + RunNppCommand(context.nppHandle, IDM_EDIT_LINE_UP); + } + + const sptr_t targetLine = currentLine + targetLineOffset; + const sptr_t targetPos = RunSci(context.editor, SCI_FINDCOLUMN, static_cast(targetLine), currentColumn); + if (targetPos >= 0) { + RunSci(context.editor, SCI_SETSEL, static_cast(targetPos), targetPos); + RunSci(context.editor, SCI_SCROLLCARET); + } + + return true; +} + +} // namespace + +bool ExecuteDuplicateLineDown(const ActionContext& context) { + return DuplicateLineWithCaretTarget(context, 1, false); +} + +bool ExecuteDuplicateLineUp(const ActionContext& context) { + return DuplicateLineWithCaretTarget(context, 0, true); +} + +bool ExecuteCutAllowLine(const ActionContext& context) { + const bool selectionEmpty = RunSci(context.editor, SCI_GETSELECTIONEMPTY) != 0; + RunSci(context.editor, selectionEmpty ? SCI_CUTALLOWLINE : SCI_CUT); + return true; +} + +bool ExecuteCopyAllowLine(const ActionContext& context) { + const bool selectionEmpty = RunSci(context.editor, SCI_GETSELECTIONEMPTY) != 0; + RunSci(context.editor, selectionEmpty ? SCI_COPYALLOWLINE : SCI_COPY); + return true; +} + +bool ExecuteSelectCurrentLine(const ActionContext& context) { + const sptr_t currentPos = RunSci(context.editor, SCI_GETCURRENTPOS); + const sptr_t line = RunSci(context.editor, SCI_LINEFROMPOSITION, static_cast(currentPos)); + const sptr_t lineStart = RunSci(context.editor, SCI_POSITIONFROMLINE, static_cast(line)); + const sptr_t lineEnd = RunSci(context.editor, SCI_GETLINEENDPOSITION, static_cast(line)); + RunSci(context.editor, SCI_SETSEL, static_cast(lineStart), lineEnd); + return true; +} + +bool ExecuteInsertLineBelow(const ActionContext& context) { + const sptr_t currentPos = RunSci(context.editor, SCI_GETCURRENTPOS); + const sptr_t line = RunSci(context.editor, SCI_LINEFROMPOSITION, static_cast(currentPos)); + const sptr_t lineEnd = RunSci(context.editor, SCI_GETLINEENDPOSITION, static_cast(line)); + RunSci(context.editor, SCI_SETSEL, static_cast(lineEnd), lineEnd); + RunSci(context.editor, SCI_NEWLINE); + return true; +} + +bool ExecuteInsertLineAbove(const ActionContext& context) { + const sptr_t currentPos = RunSci(context.editor, SCI_GETCURRENTPOS); + const sptr_t line = RunSci(context.editor, SCI_LINEFROMPOSITION, static_cast(currentPos)); + const sptr_t lineStart = RunSci(context.editor, SCI_POSITIONFROMLINE, static_cast(line)); + RunSci(context.editor, SCI_SETSEL, static_cast(lineStart), lineStart); + RunSci(context.editor, SCI_NEWLINE); + RunSci(context.editor, SCI_LINEUP); + RunSci(context.editor, SCI_HOME); + return true; +} diff --git a/src/handlers/ScrollHandlers.cpp b/src/handlers/ScrollHandlers.cpp new file mode 100644 index 0000000..56e617a --- /dev/null +++ b/src/handlers/ScrollHandlers.cpp @@ -0,0 +1,22 @@ +#include "CustomHandlers.h" + +namespace { + +bool ScrollPageNoCaret(HWND editor, sptr_t direction) { + sptr_t lines = RunSci(editor, SCI_LINESONSCREEN); + if (lines < 1) { + lines = 30; + } + RunSci(editor, SCI_LINESCROLL, 0, direction * lines); + return true; +} + +} // namespace + +bool ExecuteScrollPageUpNoCaret(const ActionContext& context) { + return ScrollPageNoCaret(context.editor, -1); +} + +bool ExecuteScrollPageDownNoCaret(const ActionContext& context) { + return ScrollPageNoCaret(context.editor, 1); +}