From 0a7f637f9d9054724bf188d704f56c527ce239f4 Mon Sep 17 00:00:00 2001 From: Jonatan Heyman Date: Tue, 22 Apr 2025 20:41:29 +0200 Subject: [PATCH] Add ability to use tabs for indentation instead of spaces --- docs/changelog.md | 2 +- electron/config.js | 2 ++ src/components/settings/Settings.vue | 9 +++++++++ src/editor/editor.js | 11 ++++++----- src/editor/indentation.js | 14 ++++++++++++++ src/stores/editor-cache.js | 4 +++- tests/tab-size-setting.spec.js | 9 +++++++++ 7 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 src/editor/indentation.js diff --git a/docs/changelog.md b/docs/changelog.md index bbb6755..881de64 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,7 +8,7 @@ Here are the most notable changes in each release. For a more detailed list of c - Added support for custom key bindings. See [the documentation](https://heynote.com/docs/#user-content-custom-key-bindings) for more info. - Added a "command palette" that can be accessed by pressing `Ctrl/Cmd+Shift+P`, or just typing `>` in the buffer selector. The command palette allows you to discover all available commands in the app, and to quickly execute them. -- Added support for configuring the tab size. +- Added support for configuring the tab size, as well as the option to use tabs instead of spaces for indentation. - Added functionality for moving blocks up and down. Default key bindings are `Ctrl/Cmd+Alt+Shift+Up` and `Ctrl/Cmd+Alt+Shift+Down`. - Added functionality for inserting the current date and time. Default key binding is `Alt+Shift+D`. diff --git a/electron/config.js b/electron/config.js index 0db2c24..e50755f 100644 --- a/electron/config.js +++ b/electron/config.js @@ -48,6 +48,7 @@ const schema = { "showInMenu": {type: "boolean", default: false}, "alwaysOnTop": {type: "boolean", default: false}, "bracketClosing": {type: "boolean", default: false}, + "indentType": {type: "string", default: "space"}, "tabSize": {type: "integer", default: 4}, "defaultBlockLanguage": {type: "string"}, "defaultBlockLanguageAutoDetect": {type: "boolean"}, @@ -87,6 +88,7 @@ const defaults = { showInMenu: false, alwaysOnTop: false, bracketClosing: false, + indentType: "space", tabSize: 4, }, theme: "system", diff --git a/src/components/settings/Settings.vue b/src/components/settings/Settings.vue index be95ab4..5a33249 100644 --- a/src/components/settings/Settings.vue +++ b/src/components/settings/Settings.vue @@ -45,6 +45,7 @@ showInMenu: this.initialSettings.showInMenu, alwaysOnTop: this.initialSettings.alwaysOnTop, bracketClosing: this.initialSettings.bracketClosing, + indentType: this.initialSettings.indentType || "space", tabSize: this.initialSettings.tabSize || 4, autoUpdate: this.initialSettings.autoUpdate, bufferPath: this.initialSettings.bufferPath, @@ -117,6 +118,7 @@ alwaysOnTop: this.alwaysOnTop, autoUpdate: this.autoUpdate, bracketClosing: this.bracketClosing, + indentType: this.indentType, tabSize: this.tabSize, bufferPath: this.bufferPath, fontFamily: this.fontFamily === defaultFontFamily ? undefined : this.fontFamily, @@ -293,6 +295,13 @@ >{{ size }} {{ size === 1 ? 'space' : 'spaces' }} +
+

Indent Using

+ +
diff --git a/src/editor/editor.js b/src/editor/editor.js index 2914858..ab1742b 100644 --- a/src/editor/editor.js +++ b/src/editor/editor.js @@ -1,8 +1,7 @@ import { Annotation, EditorState, Compartment, Facet, EditorSelection, Transaction, Prec } from "@codemirror/state" import { EditorView, keymap as cmKeymap, drawSelection, ViewPlugin, lineNumbers } from "@codemirror/view" -import { indentUnit, forceParsing, foldGutter, ensureSyntaxTree } from "@codemirror/language" +import { foldGutter, ensureSyntaxTree } from "@codemirror/language" import { markdown, markdownKeymap } from "@codemirror/lang-markdown" -import { closeBrackets } from "@codemirror/autocomplete"; import { undo, redo } from "@codemirror/commands" import { heynoteLight } from "./theme/light.js" @@ -22,6 +21,7 @@ import { languageDetection } from "./language-detection/autodetect.js" import { autoSaveContent } from "./save.js" import { todoCheckboxPlugin} from "./todo-checkbox.ts" import { links } from "./links.js" +import { indentation } from "./indentation.js" import { HEYNOTE_COMMANDS } from "./commands.js"; import { NoteFormat } from "../common/note-format.js" import { AUTO_SAVE_INTERVAL } from "../common/constants.js" @@ -43,6 +43,7 @@ export class HeynoteEditor { bracketClosing=false, fontFamily, fontSize, + indentType="space", tabSize=4, defaultBlockToken, defaultBlockAutoDetect, @@ -86,7 +87,7 @@ export class HeynoteEditor { this.themeCompartment.of(theme === "dark" ? heynoteDark : heynoteLight), heynoteBase, this.fontTheme.of(getFontTheme(fontFamily, fontSize)), - this.indentUnitCompartment.of(indentUnit.of(" ".repeat(tabSize))), + this.indentUnitCompartment.of(indentation(indentType, tabSize)), EditorView.scrollMargins.of(f => { return {top: 80, bottom: 80} }), @@ -427,9 +428,9 @@ export class HeynoteEditor { selectAll(this.view) } - setTabSize(tabSize) { + setIndentSettings(indentType, tabSize) { this.view.dispatch({ - effects: this.indentUnitCompartment.reconfigure(indentUnit.of(" ".repeat(tabSize))) + effects: this.indentUnitCompartment.reconfigure(indentation(indentType, tabSize)) }) } diff --git a/src/editor/indentation.js b/src/editor/indentation.js new file mode 100644 index 0000000..17655cd --- /dev/null +++ b/src/editor/indentation.js @@ -0,0 +1,14 @@ +import { EditorState } from "@codemirror/state" +import { indentUnit } from "@codemirror/language" + +export function indentation(indentType, tabSize) { + let unit + if (indentType === "tab") { + unit = indentUnit.of("\t") + } else if (indentType === "space") { + unit = indentUnit.of(" ".repeat(tabSize)) + } else { + throw new Error("Invalid indent type") + } + return [unit, EditorState.tabSize.of(tabSize)] +} diff --git a/src/stores/editor-cache.js b/src/stores/editor-cache.js index 4912ce5..17ae46b 100644 --- a/src/stores/editor-cache.js +++ b/src/stores/editor-cache.js @@ -36,6 +36,7 @@ export const useEditorCacheStore = defineStore("editorCache", { bracketClosing: settingsStore.settings.bracketClosing, fontFamily: settingsStore.settings.fontFamily, fontSize: settingsStore.settings.fontSize, + indentType: settingsStore.settings.indentType, tabSize: settingsStore.settings.tabSize, defaultBlockToken: settingsStore.settings.defaultBlockLanguage, defaultBlockAutoDetect: settingsStore.settings.defaultBlockLanguageAutoDetect, @@ -140,8 +141,9 @@ export const useEditorCacheStore = defineStore("editorCache", { case "fontSize": editor.setFont(newSettings.fontFamily, newSettings.fontSize) break + case "indentType": case "tabSize": - editor.setTabSize(newSettings.tabSize) + editor.setIndentSettings(newSettings.indentType, newSettings.tabSize) break case "defaultBlockLanguage": case "defaultBlockLanguageAutoDetect": diff --git a/tests/tab-size-setting.spec.js b/tests/tab-size-setting.spec.js index cb8c2cb..9d4881e 100644 --- a/tests/tab-size-setting.spec.js +++ b/tests/tab-size-setting.spec.js @@ -21,3 +21,12 @@ test("test custom tab size", async ({ page }) => { await page.locator("body").press("Tab") expect(await heynotePage.getBlockContent(0)).toBe(" ") }) + +test("test indent type", async ({ page }) => { + await page.locator("css=.status-block.settings").click() + await page.locator("css=li.tab-editing").click() + await page.locator("css=select.indent-type").selectOption("tab") + await page.locator("body").press("Escape") + await page.locator("body").press("Tab") + expect(await heynotePage.getBlockContent(0)).toBe("\t") +})