Add ability to use tabs for indentation instead of spaces

This commit is contained in:
Jonatan Heyman 2025-04-22 20:41:29 +02:00
parent 8dc630a4d6
commit 0a7f637f9d
7 changed files with 44 additions and 7 deletions

View File

@ -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 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 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 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`. - Added functionality for inserting the current date and time. Default key binding is `Alt+Shift+D`.

View File

@ -48,6 +48,7 @@ const schema = {
"showInMenu": {type: "boolean", default: false}, "showInMenu": {type: "boolean", default: false},
"alwaysOnTop": {type: "boolean", default: false}, "alwaysOnTop": {type: "boolean", default: false},
"bracketClosing": {type: "boolean", default: false}, "bracketClosing": {type: "boolean", default: false},
"indentType": {type: "string", default: "space"},
"tabSize": {type: "integer", default: 4}, "tabSize": {type: "integer", default: 4},
"defaultBlockLanguage": {type: "string"}, "defaultBlockLanguage": {type: "string"},
"defaultBlockLanguageAutoDetect": {type: "boolean"}, "defaultBlockLanguageAutoDetect": {type: "boolean"},
@ -87,6 +88,7 @@ const defaults = {
showInMenu: false, showInMenu: false,
alwaysOnTop: false, alwaysOnTop: false,
bracketClosing: false, bracketClosing: false,
indentType: "space",
tabSize: 4, tabSize: 4,
}, },
theme: "system", theme: "system",

View File

@ -45,6 +45,7 @@
showInMenu: this.initialSettings.showInMenu, showInMenu: this.initialSettings.showInMenu,
alwaysOnTop: this.initialSettings.alwaysOnTop, alwaysOnTop: this.initialSettings.alwaysOnTop,
bracketClosing: this.initialSettings.bracketClosing, bracketClosing: this.initialSettings.bracketClosing,
indentType: this.initialSettings.indentType || "space",
tabSize: this.initialSettings.tabSize || 4, tabSize: this.initialSettings.tabSize || 4,
autoUpdate: this.initialSettings.autoUpdate, autoUpdate: this.initialSettings.autoUpdate,
bufferPath: this.initialSettings.bufferPath, bufferPath: this.initialSettings.bufferPath,
@ -117,6 +118,7 @@
alwaysOnTop: this.alwaysOnTop, alwaysOnTop: this.alwaysOnTop,
autoUpdate: this.autoUpdate, autoUpdate: this.autoUpdate,
bracketClosing: this.bracketClosing, bracketClosing: this.bracketClosing,
indentType: this.indentType,
tabSize: this.tabSize, tabSize: this.tabSize,
bufferPath: this.bufferPath, bufferPath: this.bufferPath,
fontFamily: this.fontFamily === defaultFontFamily ? undefined : this.fontFamily, fontFamily: this.fontFamily === defaultFontFamily ? undefined : this.fontFamily,
@ -293,6 +295,13 @@
>{{ size }} {{ size === 1 ? 'space' : 'spaces' }}</option> >{{ size }} {{ size === 1 ? 'space' : 'spaces' }}</option>
</select> </select>
</div> </div>
<div class="entry">
<h2>Indent Using</h2>
<select v-model="indentType" @change="updateSettings" class="indent-type">
<option value="space" :selected="indentType === 'space'">Spaces</option>
<option value="tab" :selected="indentType === 'tab'">Tabs</option>
</select>
</div>
</div> </div>
<div class="row"> <div class="row">
<div class="entry"> <div class="entry">

View File

@ -1,8 +1,7 @@
import { Annotation, EditorState, Compartment, Facet, EditorSelection, Transaction, Prec } from "@codemirror/state" import { Annotation, EditorState, Compartment, Facet, EditorSelection, Transaction, Prec } from "@codemirror/state"
import { EditorView, keymap as cmKeymap, drawSelection, ViewPlugin, lineNumbers } from "@codemirror/view" 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 { markdown, markdownKeymap } from "@codemirror/lang-markdown"
import { closeBrackets } from "@codemirror/autocomplete";
import { undo, redo } from "@codemirror/commands" import { undo, redo } from "@codemirror/commands"
import { heynoteLight } from "./theme/light.js" import { heynoteLight } from "./theme/light.js"
@ -22,6 +21,7 @@ import { languageDetection } from "./language-detection/autodetect.js"
import { autoSaveContent } from "./save.js" import { autoSaveContent } from "./save.js"
import { todoCheckboxPlugin} from "./todo-checkbox.ts" import { todoCheckboxPlugin} from "./todo-checkbox.ts"
import { links } from "./links.js" import { links } from "./links.js"
import { indentation } from "./indentation.js"
import { HEYNOTE_COMMANDS } from "./commands.js"; import { HEYNOTE_COMMANDS } from "./commands.js";
import { NoteFormat } from "../common/note-format.js" import { NoteFormat } from "../common/note-format.js"
import { AUTO_SAVE_INTERVAL } from "../common/constants.js" import { AUTO_SAVE_INTERVAL } from "../common/constants.js"
@ -43,6 +43,7 @@ export class HeynoteEditor {
bracketClosing=false, bracketClosing=false,
fontFamily, fontFamily,
fontSize, fontSize,
indentType="space",
tabSize=4, tabSize=4,
defaultBlockToken, defaultBlockToken,
defaultBlockAutoDetect, defaultBlockAutoDetect,
@ -86,7 +87,7 @@ export class HeynoteEditor {
this.themeCompartment.of(theme === "dark" ? heynoteDark : heynoteLight), this.themeCompartment.of(theme === "dark" ? heynoteDark : heynoteLight),
heynoteBase, heynoteBase,
this.fontTheme.of(getFontTheme(fontFamily, fontSize)), this.fontTheme.of(getFontTheme(fontFamily, fontSize)),
this.indentUnitCompartment.of(indentUnit.of(" ".repeat(tabSize))), this.indentUnitCompartment.of(indentation(indentType, tabSize)),
EditorView.scrollMargins.of(f => { EditorView.scrollMargins.of(f => {
return {top: 80, bottom: 80} return {top: 80, bottom: 80}
}), }),
@ -427,9 +428,9 @@ export class HeynoteEditor {
selectAll(this.view) selectAll(this.view)
} }
setTabSize(tabSize) { setIndentSettings(indentType, tabSize) {
this.view.dispatch({ this.view.dispatch({
effects: this.indentUnitCompartment.reconfigure(indentUnit.of(" ".repeat(tabSize))) effects: this.indentUnitCompartment.reconfigure(indentation(indentType, tabSize))
}) })
} }

14
src/editor/indentation.js Normal file
View File

@ -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)]
}

View File

@ -36,6 +36,7 @@ export const useEditorCacheStore = defineStore("editorCache", {
bracketClosing: settingsStore.settings.bracketClosing, bracketClosing: settingsStore.settings.bracketClosing,
fontFamily: settingsStore.settings.fontFamily, fontFamily: settingsStore.settings.fontFamily,
fontSize: settingsStore.settings.fontSize, fontSize: settingsStore.settings.fontSize,
indentType: settingsStore.settings.indentType,
tabSize: settingsStore.settings.tabSize, tabSize: settingsStore.settings.tabSize,
defaultBlockToken: settingsStore.settings.defaultBlockLanguage, defaultBlockToken: settingsStore.settings.defaultBlockLanguage,
defaultBlockAutoDetect: settingsStore.settings.defaultBlockLanguageAutoDetect, defaultBlockAutoDetect: settingsStore.settings.defaultBlockLanguageAutoDetect,
@ -140,8 +141,9 @@ export const useEditorCacheStore = defineStore("editorCache", {
case "fontSize": case "fontSize":
editor.setFont(newSettings.fontFamily, newSettings.fontSize) editor.setFont(newSettings.fontFamily, newSettings.fontSize)
break break
case "indentType":
case "tabSize": case "tabSize":
editor.setTabSize(newSettings.tabSize) editor.setIndentSettings(newSettings.indentType, newSettings.tabSize)
break break
case "defaultBlockLanguage": case "defaultBlockLanguage":
case "defaultBlockLanguageAutoDetect": case "defaultBlockLanguageAutoDetect":

View File

@ -21,3 +21,12 @@ test("test custom tab size", async ({ page }) => {
await page.locator("body").press("Tab") await page.locator("body").press("Tab")
expect(await heynotePage.getBlockContent(0)).toBe(" ") 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")
})