diff --git a/.eslintignore b/.eslintignore index ea0ac5645e..53574307cf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ src/vs/ +vendor/ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1bc5515db9..b5ab0d9e39 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,6 +21,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive - name: Install Node.js uses: actions/setup-node@v4 with: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..552cdda7b0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/paredit.js"] + path = vendor/paredit.js + url = https://github.com/whitphx/paredit.js.git diff --git a/.lintstagedrc.json b/.lintstagedrc.json deleted file mode 100644 index 2a1525c7ad..0000000000 --- a/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*.{js,ts}": "eslint --cache --fix", - "*.{js,ts,md,json,yml}": "prettier --write" -} diff --git a/.prettierignore b/.prettierignore index c204f27001..e8e1e3d18f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,3 +4,4 @@ dist out src/vs/ keybindings.json +vendor/ diff --git a/.vscodeignore b/.vscodeignore index d4313bc9b8..9e46bb56c4 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -8,6 +8,7 @@ out/ src/ keybinding-generator/ scripts/ +vendor/ .gitignore .eslintignore .prettierignore @@ -26,5 +27,8 @@ yarn-debug.log* yarn-error.log* lerna-debug.log* .husky -.lintstagedrc.json +lint-staged.config.js .git-blame-ignore-revs +.eslintcache +.gitmodules +.nvmrc diff --git a/README.md b/README.md index 1bb7c8518c..ace2c4840b 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,18 @@ Set `false` when `M-` conflicts with some other necessary commands. See h When true, line-move moves point by visual lines (same as an Emacs variable line-move-visual). +### `emacs-mcx.paredit.parentheses` + +Key-value pairs of parentheses like the following example to be used in the ParEdit commands. + +```json +{ + "[": "]", + "(": ")", + "{": "}" +} +``` + ### `emacs-mcx.debug.*` Configurations for debugging. diff --git a/lint-staged.config.js b/lint-staged.config.js new file mode 100644 index 0000000000..9d0bec4956 --- /dev/null +++ b/lint-staged.config.js @@ -0,0 +1,22 @@ +/* eslint-env node */ +/* eslint-disable @typescript-eslint/no-var-requires */ + +const path = require("path"); +const micromatch = require("micromatch"); + +module.exports = { + "*.{js,ts,mjs,mts}": (files) => { + const match = micromatch.not(files, path.join(__dirname, "./vendor/*")); + if (match.length === 0) { + return []; + } + return `eslint --cache --fix ${match.join(" ")}`; + }, + "*.{js,ts,mjs,mts,md,json,yml}": (files) => { + const match = micromatch.not(files, path.join(__dirname, "./vendor/*")); + if (match.length === 0) { + return []; + } + return `prettier --write ${match.join(" ")}`; + }, +}; diff --git a/package.json b/package.json index 3436b48979..08d61b1b71 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,20 @@ "default": true, "description": "When true, line-move moves point by visual lines (same as an Emacs variable line-move-visual)." }, + "emacs-mcx.paredit.parentheses": { + "type": "object", + "patternProperties": { + "^\\S$": { + "type": "string", + "pattern": "^\\S$" + } + }, + "default": { + "[": "]", + "(": ")", + "{": "}" + } + }, "emacs-mcx.debug.silent": { "type": "boolean", "description": "If true, all logs are suppressed.", @@ -6695,7 +6709,7 @@ "webpack-cli": "^5.1.4" }, "dependencies": { - "paredit.js": "^0.4.0", + "paredit.js": "./vendor/paredit.js", "winston": "^3.11.0", "winston-console-for-electron": "^0.0.7" } diff --git a/src/configuration/configuration.ts b/src/configuration/configuration.ts index dc2cd97ee0..5c8de054b3 100644 --- a/src/configuration/configuration.ts +++ b/src/configuration/configuration.ts @@ -4,7 +4,8 @@ import { Logger } from "../logger"; import * as vscode from "vscode"; -import { IConfiguration, IDebugConfiguration } from "./iconfiguration"; +import { IConfiguration, IDebugConfiguration, IPareditConfiguration } from "./iconfiguration"; +import * as paredit from "paredit.js"; export class Configuration implements IConfiguration, vscode.Disposable { /** @@ -40,6 +41,10 @@ export class Configuration implements IConfiguration, vscode.Disposable { public lineMoveVisual = true; + public paredit: IPareditConfiguration = { + parentheses: { "[": "]", "(": ")", "{": "}" }, + }; + public debug: IDebugConfiguration = { silent: false, loggingLevelForAlert: "error", @@ -77,6 +82,9 @@ export class Configuration implements IConfiguration, vscode.Disposable { } Logger.configChanged(this); + + // Update configs in the third-party libraries. + paredit.reader.setParentheses(this.paredit.parentheses); } private static unproxify(obj: { [key: string]: unknown }) { diff --git a/src/configuration/iconfiguration.ts b/src/configuration/iconfiguration.ts index 97cdde735b..ae11d34ba1 100644 --- a/src/configuration/iconfiguration.ts +++ b/src/configuration/iconfiguration.ts @@ -1,3 +1,7 @@ +export interface IPareditConfiguration { + parentheses: { [key: string]: string }; +} + export interface IDebugConfiguration { /** * Boolean indicating whether all logs should be suppressed @@ -39,6 +43,11 @@ export interface IConfiguration { */ lineMoveVisual: boolean; + /** + * Paredit configuration + */ + paredit: IPareditConfiguration; + /** * Extension debugging settings */ diff --git a/src/test/suite/commands/paredit.test.ts b/src/test/suite/commands/paredit.test.ts index 1d6d488ea9..91064318d2 100644 --- a/src/test/suite/commands/paredit.test.ts +++ b/src/test/suite/commands/paredit.test.ts @@ -1,4 +1,6 @@ import assert from "assert"; +import * as vscode from "vscode"; +import * as sinon from "sinon"; import { Selection, TextEditor } from "vscode"; import { EmacsEmulator } from "../../../emulator"; import { KillRing } from "../../../kill-yank/kill-ring"; @@ -11,6 +13,7 @@ import { clearTextEditor, assertSelectionsEqual, } from "../utils"; +import { Configuration } from "../../../configuration/configuration"; suite("paredit commands", () => { let activeTextEditor: TextEditor; @@ -64,6 +67,51 @@ suite("paredit commands", () => { }); }); +suite("Parentheses config", () => { + let activeTextEditor: TextEditor; + let emulator: EmacsEmulator; + let getConfigurationStub: sinon.SinonStub; + + setup(async () => { + const initialText = "<<>>"; + + activeTextEditor = await setupWorkspace(initialText); + emulator = new EmacsEmulator(activeTextEditor); + + getConfigurationStub = sinon.stub(vscode.workspace, "getConfiguration"); + }); + teardown(async () => { + getConfigurationStub.restore(); + Configuration.reload(); + await cleanUpWorkspace(); + }); + + function mockPareditConfig(parentheses: Record) { + getConfigurationStub.returns({ + paredit: { + parentheses, + }, + }); + Configuration.reload(); + } + + test("forwardSexp", async () => { + mockPareditConfig({ "(": ")" }); + setEmptyCursors(activeTextEditor, [0, 1]); + await emulator.runCommand("paredit.forwardSexp"); + assertCursorsEqual(activeTextEditor, [0, 4]); + await emulator.runCommand("paredit.forwardSexp"); + assertCursorsEqual(activeTextEditor, [0, 4]); + + mockPareditConfig({ "<": ">" }); + setEmptyCursors(activeTextEditor, [0, 1]); + await emulator.runCommand("paredit.forwardSexp"); + assertCursorsEqual(activeTextEditor, [0, 3]); + await emulator.runCommand("paredit.forwardSexp"); + assertCursorsEqual(activeTextEditor, [0, 3]); + }); +}); + suite("paredit.kill-sexp", () => { const initialText = `( ( diff --git a/vendor/paredit.js b/vendor/paredit.js new file mode 160000 index 0000000000..a6f9bd3085 --- /dev/null +++ b/vendor/paredit.js @@ -0,0 +1 @@ +Subproject commit a6f9bd3085e8bd73932d5de2b7f38a228baf4915 diff --git a/yarn.lock b/yarn.lock index e95efa3fe9..031e9564a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3104,10 +3104,8 @@ pako@~1.0.2: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== -paredit.js@^0.4.0: +paredit.js@./vendor/paredit.js: version "0.4.0" - resolved "https://registry.yarnpkg.com/paredit.js/-/paredit.js-0.4.0.tgz#a4bb7eeef8d4e5601d2dcc3f7929e5fea1e4f240" - integrity sha512-Zw1yo0DzKOjoI/VNoHINrJ9zis2it188XJRdjt2yAusSqr4Ch2Vq0hbzpqKEa+BMtWBHCVlfzpgXpM5b1TPVbA== dependencies: ace.improved ">=0.1.4"