Add setting for configuring the default block language and language auto detection

This commit is contained in:
Jonatan Heyman 2024-07-12 14:33:26 +02:00
parent e5d4d31ca2
commit d82b3920d7
8 changed files with 89 additions and 25 deletions

View File

@ -35,6 +35,8 @@ 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},
"defaultBlockLanguage": {type: "string"},
"defaultBlockLanguageAutoDetect": {type: "boolean"},
// when default font settings are used, fontFamily and fontSize is not specified in the // when default font settings are used, fontFamily and fontSize is not specified in the
// settings file, so that it's possible for us to change the default settings in the // settings file, so that it's possible for us to change the default settings in the

View File

@ -122,6 +122,8 @@
:bracketClosing="settings.bracketClosing" :bracketClosing="settings.bracketClosing"
:fontFamily="settings.fontFamily" :fontFamily="settings.fontFamily"
:fontSize="settings.fontSize" :fontSize="settings.fontSize"
:defaultBlockLanguage="settings.defaultBlockLanguage || 'text'"
:defaultBlockLanguageAutoDetect="settings.defaultBlockLanguageAutoDetect === undefined ? true : settings.defaultBlockLanguageAutoDetect"
class="editor" class="editor"
ref="editor" ref="editor"
@openLanguageSelector="openLanguageSelector" @openLanguageSelector="openLanguageSelector"

View File

@ -29,6 +29,8 @@
}, },
fontFamily: String, fontFamily: String,
fontSize: Number, fontSize: Number,
defaultBlockLanguage: String,
defaultBlockLanguageAutoDetect: Boolean,
}, },
components: {}, components: {},
@ -78,6 +80,7 @@
}) })
window._heynote_editor = this.editor window._heynote_editor = this.editor
window.document.addEventListener("currenciesLoaded", this.onCurrenciesLoaded) window.document.addEventListener("currenciesLoaded", this.onCurrenciesLoaded)
this.editor.setDefaultBlockLanguage(this.defaultBlockLanguage, this.defaultBlockLanguageAutoDetect)
// set up buffer change listener // set up buffer change listener
window.heynote.buffer.onChangeCallback((event, content) => { window.heynote.buffer.onChangeCallback((event, content) => {
@ -145,12 +148,18 @@
fontSize() { fontSize() {
this.editor.setFont(this.fontFamily, this.fontSize) this.editor.setFont(this.fontFamily, this.fontSize)
}, },
defaultBlockLanguage() {
this.editor.setDefaultBlockLanguage(this.defaultBlockLanguage, this.defaultBlockLanguageAutoDetect)
},
defaultBlockLanguageAutoDetect() {
this.editor.setDefaultBlockLanguage(this.defaultBlockLanguage, this.defaultBlockLanguageAutoDetect)
},
}, },
methods: { methods: {
setLanguage(language) { setLanguage(language) {
if (language === "auto") { if (language === "auto") {
this.editor.setCurrentLanguage("text", true) this.editor.setCurrentLanguage(null, true)
} else { } else {
this.editor.setCurrentLanguage(language, false) this.editor.setCurrentLanguage(language, false)
} }

View File

@ -1,10 +1,14 @@
<script> <script>
import { LANGUAGES } from '../../editor/languages.js'
import KeyboardHotkey from "./KeyboardHotkey.vue" import KeyboardHotkey from "./KeyboardHotkey.vue"
import TabListItem from "./TabListItem.vue" import TabListItem from "./TabListItem.vue"
import TabContent from "./TabContent.vue" import TabContent from "./TabContent.vue"
const defaultFontFamily = window.heynote.defaultFontFamily const defaultFontFamily = window.heynote.defaultFontFamily
const defaultFontSize = window.heynote.defaultFontSize const defaultFontSize = window.heynote.defaultFontSize
const defaultDefaultBlockLanguage = "text"
const defaultDefaultBlockLanguageAutoDetect = true
export default { export default {
props: { props: {
@ -39,6 +43,16 @@
bufferPath: this.initialSettings.bufferPath, bufferPath: this.initialSettings.bufferPath,
fontFamily: this.initialSettings.fontFamily || defaultFontFamily, fontFamily: this.initialSettings.fontFamily || defaultFontFamily,
fontSize: this.initialSettings.fontSize || defaultFontSize, fontSize: this.initialSettings.fontSize || defaultFontSize,
languageOptions: LANGUAGES.map(l => {
return {
"value": l.token,
"name": l.token == "text" ? l.name + " (default)" : l.name,
}
}).sort((a, b) => {
return a.name.localeCompare(b.name)
}),
defaultBlockLanguage: this.initialSettings.defaultBlockLanguage || defaultDefaultBlockLanguage,
defaultBlockLanguageAutoDetect: this.initialSettings.defaultBlockLanguageAutoDetect === false ? false : defaultDefaultBlockLanguageAutoDetect,
activeTab: "general", activeTab: "general",
isWebApp: window.heynote.platform.isWebApp, isWebApp: window.heynote.platform.isWebApp,
@ -89,6 +103,8 @@
bufferPath: this.bufferPath, bufferPath: this.bufferPath,
fontFamily: this.fontFamily === defaultFontFamily ? undefined : this.fontFamily, fontFamily: this.fontFamily === defaultFontFamily ? undefined : this.fontFamily,
fontSize: this.fontSize === defaultFontSize ? undefined : this.fontSize, fontSize: this.fontSize === defaultFontSize ? undefined : this.fontSize,
defaultBlockLanguage: this.defaultBlockLanguage === "text" ? undefined : this.defaultBlockLanguage,
defaultBlockLanguageAutoDetect: this.defaultBlockLanguageAutoDetect === true ? undefined : this.defaultBlockLanguageAutoDetect,
}) })
if (!this.showInDock) { if (!this.showInDock) {
this.showInMenu = true this.showInMenu = true
@ -255,6 +271,24 @@
</label> </label>
</div> </div>
</div> </div>
<div class="row">
<div class="entry">
<h2>Default Block Language</h2>
<select v-model="defaultBlockLanguage" @change="updateSettings" class="block-language">
<template v-for="lang in languageOptions" :key="lang.value">
<option :selected="lang.value === defaultBlockLanguage" :value="lang.value">{{ lang.name }}</option>
</template>
</select>
<label>
<input
type="checkbox"
v-model="defaultBlockLanguageAutoDetect"
@change="updateSettings"
/>
Auto-detection (default: on)
</label>
</div>
</div>
</TabContent> </TabContent>
<TabContent tab="appearance" :activeTab="activeTab"> <TabContent tab="appearance" :activeTab="activeTab">
@ -417,6 +451,7 @@
overflow-y: auto overflow-y: auto
select select
height: 22px height: 22px
margin: 4px 0
.row .row
display: flex display: flex
.entry .entry

View File

@ -7,7 +7,11 @@ import { selectAll } from "./select-all.js";
export { moveLineDown, moveLineUp, selectAll } export { moveLineDown, moveLineUp, selectAll }
export const insertNewBlockAtCursor = ({ state, dispatch }) => { function getBlockDelimiter(defaultToken, autoDetect) {
return `\n∞∞∞${autoDetect ? defaultToken + '-a' : defaultToken}\n`
}
export const insertNewBlockAtCursor = (editor) => ({ state, dispatch }) => {
if (state.readOnly) if (state.readOnly)
return false return false
@ -16,7 +20,7 @@ export const insertNewBlockAtCursor = ({ state, dispatch }) => {
if (currentBlock) { if (currentBlock) {
delimText = `\n∞∞∞${currentBlock.language.name}${currentBlock.language.auto ? "-a" : ""}\n` delimText = `\n∞∞∞${currentBlock.language.name}${currentBlock.language.auto ? "-a" : ""}\n`
} else { } else {
delimText = "\n∞∞∞text-a\n" delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
} }
dispatch(state.replaceSelection(delimText), dispatch(state.replaceSelection(delimText),
{ {
@ -28,13 +32,12 @@ export const insertNewBlockAtCursor = ({ state, dispatch }) => {
return true; return true;
} }
export const addNewBlockBeforeCurrent = ({ state, dispatch }) => { export const addNewBlockBeforeCurrent = (editor) => ({ state, dispatch }) => {
console.log("addNewBlockBeforeCurrent")
if (state.readOnly) if (state.readOnly)
return false return false
const block = getActiveNoteBlock(state) const block = getActiveNoteBlock(state)
const delimText = "\n∞∞∞text-a\n" const delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
dispatch(state.update({ dispatch(state.update({
changes: { changes: {
@ -50,12 +53,12 @@ export const addNewBlockBeforeCurrent = ({ state, dispatch }) => {
return true; return true;
} }
export const addNewBlockAfterCurrent = ({ state, dispatch }) => { export const addNewBlockAfterCurrent = (editor) => ({ state, dispatch }) => {
if (state.readOnly) if (state.readOnly)
return false return false
const block = getActiveNoteBlock(state) const block = getActiveNoteBlock(state)
const delimText = "\n∞∞∞text-a\n" const delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
dispatch(state.update({ dispatch(state.update({
changes: { changes: {
@ -70,12 +73,12 @@ export const addNewBlockAfterCurrent = ({ state, dispatch }) => {
return true; return true;
} }
export const addNewBlockBeforeFirst = ({ state, dispatch }) => { export const addNewBlockBeforeFirst = (editor) => ({ state, dispatch }) => {
if (state.readOnly) if (state.readOnly)
return false return false
const block = getFirstNoteBlock(state) const block = getFirstNoteBlock(state)
const delimText = "\n∞∞∞text-a\n" const delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
dispatch(state.update({ dispatch(state.update({
changes: { changes: {
@ -91,11 +94,11 @@ export const addNewBlockBeforeFirst = ({ state, dispatch }) => {
return true; return true;
} }
export const addNewBlockAfterLast = ({ state, dispatch }) => { export const addNewBlockAfterLast = (editor) => ({ state, dispatch }) => {
if (state.readOnly) if (state.readOnly)
return false return false
const block = getLastNoteBlock(state) const block = getLastNoteBlock(state)
const delimText = "\n∞∞∞text-a\n" const delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
dispatch(state.update({ dispatch(state.update({
changes: { changes: {
@ -131,6 +134,10 @@ export function changeLanguageTo(state, dispatch, block, language, auto) {
export function changeCurrentBlockLanguage(state, dispatch, language, auto) { export function changeCurrentBlockLanguage(state, dispatch, language, auto) {
const block = getActiveNoteBlock(state) const block = getActiveNoteBlock(state)
// if language is null, we only want to change the auto-detect flag
if (language === null) {
language = block.language.name
}
changeLanguageTo(state, dispatch, block, language, auto) changeLanguageTo(state, dispatch, block, language, auto)
} }

View File

@ -1,4 +1,4 @@
import { Annotation, EditorState, Compartment } from "@codemirror/state" import { Annotation, EditorState, Compartment, Facet } from "@codemirror/state"
import { EditorView, keymap, drawSelection, ViewPlugin, lineNumbers } from "@codemirror/view" import { EditorView, keymap, drawSelection, ViewPlugin, lineNumbers } from "@codemirror/view"
import { indentUnit, forceParsing, foldGutter } from "@codemirror/language" import { indentUnit, forceParsing, foldGutter } from "@codemirror/language"
import { markdown } from "@codemirror/lang-markdown" import { markdown } from "@codemirror/lang-markdown"
@ -59,6 +59,8 @@ export class HeynoteEditor {
this.deselectOnCopy = keymap === "emacs" this.deselectOnCopy = keymap === "emacs"
this.emacsMetaKey = emacsMetaKey this.emacsMetaKey = emacsMetaKey
this.fontTheme = new Compartment this.fontTheme = new Compartment
this.defaultBlockToken = "text"
this.defaultBlockAutoDetect = true
const state = EditorState.create({ const state = EditorState.create({
doc: content || "", doc: content || "",
@ -84,7 +86,7 @@ export class HeynoteEditor {
}), }),
heynoteLang(), heynoteLang(),
noteBlockExtension(this), noteBlockExtension(this),
languageDetection(() => this.view), languageDetection(() => this),
// set cursor blink rate to 1 second // set cursor blink rate to 1 second
drawSelection({cursorBlinkRate:1000}), drawSelection({cursorBlinkRate:1000}),
@ -206,6 +208,11 @@ export class HeynoteEditor {
}) })
} }
setDefaultBlockLanguage(token, autoDetect) {
this.defaultBlockToken = token
this.defaultBlockAutoDetect = autoDetect
}
formatCurrentBlock() { formatCurrentBlock() {
formatBlockContent({ formatBlockContent({
state: this.view.state, state: this.view.state,

View File

@ -48,11 +48,11 @@ export function heynoteKeymap(editor) {
["Mod-x", cutCommand(editor)], ["Mod-x", cutCommand(editor)],
["Tab", indentMore], ["Tab", indentMore],
["Shift-Tab", indentLess], ["Shift-Tab", indentLess],
["Alt-Shift-Enter", addNewBlockBeforeFirst], ["Alt-Shift-Enter", addNewBlockBeforeFirst(editor)],
["Mod-Shift-Enter", addNewBlockAfterLast], ["Mod-Shift-Enter", addNewBlockAfterLast(editor)],
["Alt-Enter", addNewBlockBeforeCurrent], ["Alt-Enter", addNewBlockBeforeCurrent(editor)],
["Mod-Enter", addNewBlockAfterCurrent], ["Mod-Enter", addNewBlockAfterCurrent(editor)],
["Mod-Alt-Enter", insertNewBlockAtCursor], ["Mod-Alt-Enter", insertNewBlockAtCursor(editor)],
["Mod-a", selectAll], ["Mod-a", selectAll],
["Alt-ArrowUp", moveLineUp], ["Alt-ArrowUp", moveLineUp],
["Alt-ArrowDown", moveLineDown], ["Alt-ArrowDown", moveLineDown],

View File

@ -25,7 +25,7 @@ function cancelIdleCallbackCompat(id) {
} }
} }
export function languageDetection(getView) { export function languageDetection(getEditor) {
const previousBlockContent = {} const previousBlockContent = {}
let idleCallbackId = null let idleCallbackId = null
@ -35,7 +35,8 @@ export function languageDetection(getView) {
if (!event.data.guesslang.language) { if (!event.data.guesslang.language) {
return return
} }
const view = getView() const editor = getEditor()
const view = editor.view
const state = view.state const state = view.state
const block = getActiveNoteBlock(state) const block = getActiveNoteBlock(state)
const newLang = GUESSLANG_TO_TOKEN[event.data.guesslang.language] const newLang = GUESSLANG_TO_TOKEN[event.data.guesslang.language]
@ -88,11 +89,12 @@ export function languageDetection(getView) {
const content = update.state.doc.sliceString(block.content.from, block.content.to) const content = update.state.doc.sliceString(block.content.from, block.content.to)
if (content === "" && redoDepth(update.state) === 0) { if (content === "" && redoDepth(update.state) === 0) {
// if content is cleared, set language to plaintext // if content is cleared, set language to default
const view = getView() const editor = getEditor()
const view = editor.view
const block = getActiveNoteBlock(view.state) const block = getActiveNoteBlock(view.state)
if (block.language.name !== "text") { if (block.language.name !== editor.defaultBlockToken) {
changeLanguageTo(view.state, view.dispatch, block, "text", true) changeLanguageTo(view.state, view.dispatch, block, editor.defaultBlockToken, true)
} }
delete previousBlockContent[idx] delete previousBlockContent[idx]
} }