mirror of
https://github.com/heyman/heynote.git
synced 2024-11-24 08:53:11 +01:00
Merge branch 'main' into patch-1
This commit is contained in:
commit
e84b36273a
94
README.md
94
README.md
@ -3,6 +3,12 @@
|
||||
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/heyman/heynote)](https://github.com/heyman/heynote/releases)
|
||||
[![Build Status](https://github.com/heyman/heynote/workflows/Tests/badge.svg)](https://github.com/heyman/heynote/actions?query=workflow%3ATests)
|
||||
|
||||
<img src="https://heynote.com/img/logo.png" style="width:79px;">
|
||||
|
||||
## General Information
|
||||
|
||||
- Website: [heynote.com](https://heynote.com)
|
||||
- Documentation: [heynote.com](https://heynote.com/docs/)
|
||||
|
||||
Heynote is a dedicated scratchpad for developers. It functions as a large persistent text buffer where you can write down anything you like. Works great for that Slack message you don't want to accidentally send, a JSON response from an API you're working with, notes from a meeting, your daily to-do list, etc.
|
||||
|
||||
@ -16,7 +22,7 @@ Available for Mac, Windows, and Linux.
|
||||
- Block-based
|
||||
- Syntax highlighting:
|
||||
|
||||
C++, C#, Clojure, CSS, Erlang, Go, Groovy, HTML, Java, JavaScript, JSX, Kotlin, TypeScript, TOML, TSX, JSON, Lezer, Markdown, PHP, Python, Ruby, Rust, Shell, SQL, Swift, XML, YAML
|
||||
C++, C#, Clojure, CSS, Erlang, Dart, Go, Groovy, HTML, Java, JavaScript, JSX, Kotlin, TypeScript, TOML, TSX, JSON, Lezer, Markdown, PHP, Python, Ruby, Rust, Shell, SQL, Swift, XML, YAML
|
||||
|
||||
- Language auto-detection
|
||||
- Auto-formatting
|
||||
@ -28,19 +34,9 @@ Available for Mac, Windows, and Linux.
|
||||
- Default or Emacs-like key bindings
|
||||
|
||||
|
||||
## Installation
|
||||
## Documentation
|
||||
|
||||
Download the appropriate (Mac, Windows or Linux) version from the latest Github release (or from [heynote.com](https://heynote.com)). The Windows build is not signed, so you might see some scary warning (I can not justify paying a yearly fee for a certificate just to get rid of that).
|
||||
|
||||
### Notes on Linux installation
|
||||
|
||||
It's been reported [(#48)](https://github.com/heyman/heynote/issues/48) that ChromeOS's Debian VM need the following packages installed to run the Heynote AppImage:
|
||||
|
||||
```
|
||||
libfuse2
|
||||
libnss3
|
||||
libnspr4
|
||||
```
|
||||
[Documentation](https://heynote.com/docs/) is available on the Heynote website.
|
||||
|
||||
## Development
|
||||
|
||||
@ -70,49 +66,12 @@ To run the tests in the Playwright UI:
|
||||
|
||||
I'm happy to merge contributions that fit my vision for the app. Bug fixes are always welcome.
|
||||
|
||||
## Math Blocks
|
||||
|
||||
Heynote's Math blocks are powered by [Math.js expressions](https://mathjs.org/docs/expressions). Checkout their [documentation](https://mathjs.org/docs/) to see what [syntax](https://mathjs.org/docs/expressions/syntax.html), [functions](https://mathjs.org/docs/reference/functions.html), and [constants](https://mathjs.org/docs/reference/constants.html) are available.
|
||||
|
||||
### Accessing the previous result
|
||||
|
||||
The variable `prev` can be used to access the previous result. For example:
|
||||
|
||||
```
|
||||
128
|
||||
prev * 2 # 256
|
||||
```
|
||||
|
||||
### Changing how the results of Math blocks are formatted?
|
||||
|
||||
You can define a custom `format` function within the Math block like this:
|
||||
|
||||
```
|
||||
_format = format # store reference to the built in format
|
||||
format(x) = _format(x, {notation:"exponential"})
|
||||
```
|
||||
|
||||
You can also do something like this to show the number with your default locale or provide a [custom one](https://www.w3.org/International/articles/language-tags/):
|
||||
|
||||
```
|
||||
format(x) = x.toLocaleString();
|
||||
format(x) = x.toLocaleString('en-GB');
|
||||
```
|
||||
|
||||
See the [Math.js format()](https://mathjs.org/docs/reference/functions/format.html) function for more info on what's supported.
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
### Where is the buffer data stored?
|
||||
|
||||
The default paths for the buffer data for the respective OS are:
|
||||
|
||||
- Mac: `~/Library/Application Support/Heynote/buffer.txt`
|
||||
- Windows: `%APPDATA%\Heynote\buffer.txt`
|
||||
- Linux: `~/.config/Heynote/buffer.txt`
|
||||
|
||||
From version >=1.5.0, symlinks will be supported and you'll be able to configure the path where `buffer.txt` is stored.
|
||||
See the [documentation](https://heynote.com/docs/#user-content-the-buffer-file).
|
||||
|
||||
### Can you make a mobile app?
|
||||
|
||||
@ -126,38 +85,7 @@ I can totally see the usefulness of such a feature, and it's definitely somethin
|
||||
|
||||
### What are the default keyboard shortcuts?
|
||||
|
||||
**On Mac**
|
||||
|
||||
```
|
||||
⌘ + Enter Add new block below the current block
|
||||
⌥ + Enter Add new block before the current block
|
||||
⌘ + Shift + Enter Add new block at the end of the buffer
|
||||
⌥ + Shift + Enter Add new block at the start of the buffer
|
||||
⌘ + ⌥ + Enter Split the current block at cursor position
|
||||
⌘ + L Change block language
|
||||
⌘ + Down Goto next block
|
||||
⌘ + Up Goto previous block
|
||||
⌘ + A Select all text in a note block. Press again to select the whole buffer
|
||||
⌘ + ⌥ + Up/Down Add additional cursor above/below
|
||||
⌥ + Shift + F Format block content (works for JSON, JavaScript, HTML, CSS and Markdown)
|
||||
```
|
||||
|
||||
**On Windows and Linux**
|
||||
|
||||
```
|
||||
Ctrl + Enter Add new block below the current block
|
||||
Alt + Enter Add new block before the current block
|
||||
Ctrl + Shift + Enter Add new block at the end of the buffer
|
||||
Alt + Shift + Enter Add new block at the start of the buffer
|
||||
Ctrl + Alt + Enter Split the current block at cursor position
|
||||
Ctrl + L Change block language
|
||||
Ctrl + Down Goto next block
|
||||
Ctrl + Up Goto previous block
|
||||
Ctrl + A Select all text in a note block. Press again to select the whole buffer
|
||||
Ctrl + Alt + Up/Down Add additional cursor above/below
|
||||
Alt + Shift + F Format block content (works for JSON, JavaScript, HTML, CSS and Markdown)
|
||||
Alt Show menu
|
||||
```
|
||||
See the [documentation](https://heynote.com/docs/#user-content-default-key-bindings).
|
||||
|
||||
## Thanks!
|
||||
|
||||
|
89
docs/index.md
Normal file
89
docs/index.md
Normal file
@ -0,0 +1,89 @@
|
||||
# Heynote Documentation
|
||||
|
||||
Heynote is a dedicated scratchpad for developers. It functions as a large persistent text buffer where you can write down anything you like. Works great for that Slack message you don't want to accidentally send, a JSON response from an API you're working with, notes from a meeting, your daily to-do list, etc.
|
||||
|
||||
The Heynote buffer is divided into blocks, and each block can have its own Language set (e.g. JavaScript, JSON, Markdown, etc.). This gives you syntax highlighting and lets you auto-format that JSON response.
|
||||
|
||||
## Default Key Bindings
|
||||
|
||||
<!-- keyboard_shortcuts -->
|
||||
|
||||
**On Mac**
|
||||
|
||||
```
|
||||
⌘ + Enter Add new block below the current block
|
||||
⌥ + Enter Add new block before the current block
|
||||
⌘ + Shift + Enter Add new block at the end of the buffer
|
||||
⌥ + Shift + Enter Add new block at the start of the buffer
|
||||
⌘ + ⌥ + Enter Split the current block at cursor position
|
||||
⌘ + L Change block language
|
||||
⌘ + Down Goto next block
|
||||
⌘ + Up Goto previous block
|
||||
⌘ + A Select all text in a note block. Press again to select the whole buffer
|
||||
⌘ + ⌥ + Up/Down Add additional cursor above/below
|
||||
⌥ + Shift + F Format block content (works for JSON, JavaScript, HTML, CSS and Markdown)
|
||||
```
|
||||
|
||||
**On Windows and Linux**
|
||||
|
||||
```
|
||||
Ctrl + Enter Add new block below the current block
|
||||
Alt + Enter Add new block before the current block
|
||||
Ctrl + Shift + Enter Add new block at the end of the buffer
|
||||
Alt + Shift + Enter Add new block at the start of the buffer
|
||||
Ctrl + Alt + Enter Split the current block at cursor position
|
||||
Ctrl + L Change block language
|
||||
Ctrl + Down Goto next block
|
||||
Ctrl + Up Goto previous block
|
||||
Ctrl + A Select all text in a note block. Press again to select the whole buffer
|
||||
Ctrl + Alt + Up/Down Add additional cursor above/below
|
||||
Alt + Shift + F Format block content (works for JSON, JavaScript, HTML, CSS and Markdown)
|
||||
Alt Show menu
|
||||
```
|
||||
|
||||
## Download/Installation
|
||||
|
||||
Download the appropriate (Mac, Windows or Linux) version from [heynote.com](https://heynote.com). The Windows build is not signed, so you might see some scary warning (I can not justify paying a yearly fee for a certificate just to get rid of that).
|
||||
|
||||
### Notes on Linux installation
|
||||
|
||||
It's been reported [(#48)](https://github.com/heyman/heynote/issues/48) that ChromeOS's Debian VM need the following packages installed to run the Heynote AppImage:
|
||||
|
||||
```
|
||||
libfuse2
|
||||
libnss3
|
||||
libnspr4
|
||||
```
|
||||
|
||||
## Math Blocks
|
||||
|
||||
Heynote's Math blocks are powered by [Math.js expressions](https://mathjs.org/docs/expressions). Checkout their [documentation](https://mathjs.org/docs/) to see what [syntax](https://mathjs.org/docs/expressions/syntax.html), [functions](https://mathjs.org/docs/reference/functions.html), and [constants](https://mathjs.org/docs/reference/constants.html) are available.
|
||||
|
||||
### Accessing the previous result
|
||||
|
||||
The variable `prev` can be used to access the previous result. For example:
|
||||
|
||||
```
|
||||
128
|
||||
prev * 2 # 256
|
||||
```
|
||||
|
||||
### Changing how the results of Math blocks are formatted
|
||||
|
||||
You can define a custom `format` function within the Math block like this:
|
||||
|
||||
```
|
||||
_format = format # store reference to the built in format
|
||||
format(x) = _format(x, {notation:"exponential"})
|
||||
```
|
||||
|
||||
See the [Math.js format()](https://mathjs.org/docs/reference/functions/format.html) function for more info on what's supported.
|
||||
|
||||
## The buffer file
|
||||
|
||||
The default paths for the buffer data for the respective operating systems are:
|
||||
|
||||
- Mac: `~/Library/Application Support/Heynote/buffer.txt`
|
||||
- Windows: `%APPDATA%\Heynote\buffer.txt`
|
||||
- Linux: `~/.config/Heynote/buffer.txt`
|
||||
|
@ -35,6 +35,8 @@ const schema = {
|
||||
"showInMenu": {type: "boolean", default: false},
|
||||
"alwaysOnTop": {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
|
||||
// settings file, so that it's possible for us to change the default settings in the
|
||||
|
@ -131,7 +131,14 @@ const template = [
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
label: 'Documentation',
|
||||
click: async () => {
|
||||
const { shell } = require('electron')
|
||||
await shell.openExternal('https://heynote.com/docs/')
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Website',
|
||||
click: async () => {
|
||||
const { shell } = require('electron')
|
||||
await shell.openExternal('https://heynote.com')
|
||||
|
72
package-lock.json
generated
72
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "Heynote",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.0-beta",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "Heynote",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.0-beta",
|
||||
"license": "Commons Clause MIT",
|
||||
"dependencies": {
|
||||
"electron-log": "^5.0.1"
|
||||
@ -25,6 +25,7 @@
|
||||
"@codemirror/lang-python": "^6.1.3",
|
||||
"@codemirror/lang-rust": "^6.0.1",
|
||||
"@codemirror/lang-sql": "^6.5.4",
|
||||
"@codemirror/lang-vue": "^0.1.3",
|
||||
"@codemirror/lang-xml": "^6.0.2",
|
||||
"@codemirror/language": "^6.9.3",
|
||||
"@codemirror/legacy-modes": "^6.3.3",
|
||||
@ -41,13 +42,13 @@
|
||||
"@types/node": "^20.10.5",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"debounce": "^1.2.1",
|
||||
"electron": "^28.0.0",
|
||||
"electron": "^31.1.0",
|
||||
"electron-builder": "^23.6.0",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
"fs-jetpack": "^5.1.0",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier": "^3.3.2",
|
||||
"rollup-plugin-license": "^3.0.1",
|
||||
"sass": "^1.57.1",
|
||||
"typescript": "^4.9.4",
|
||||
@ -427,6 +428,20 @@
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-vue": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-vue/-/lang-vue-0.1.3.tgz",
|
||||
"integrity": "sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@codemirror/lang-html": "^6.0.0",
|
||||
"@codemirror/lang-javascript": "^6.1.2",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-xml": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.0.2.tgz",
|
||||
@ -992,9 +1007,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.2.tgz",
|
||||
"integrity": "sha512-V+GqBsga5+cQJMfM0GdnHmg4DgWvLzgMWjbldBg0+jC3k9Gu6nJNZDLJxXEBT1Xj8KhRN4jmbC5CY7SIL++sVw==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz",
|
||||
"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@lezer/cpp": {
|
||||
@ -2262,12 +2277,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -3063,9 +3078,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
||||
"integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"jake": "^10.8.5"
|
||||
@ -3078,14 +3093,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "28.0.0",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-28.0.0.tgz",
|
||||
"integrity": "sha512-eDhnCFBvG0PGFVEpNIEdBvyuGUBsFdlokd+CtuCe2ER3P+17qxaRfWRxMmksCOKgDHb5Wif5UxqOkZSlA4snlw==",
|
||||
"version": "31.1.0",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-31.1.0.tgz",
|
||||
"integrity": "sha512-TBOwqLxSxnx6+pH6GMri7R3JPH2AkuGJHfWZS0p1HsmN+Qr1T9b0IRJnnehSd/3NZAmAre4ft9Ljec7zjyKFJA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/node": "^20.9.0",
|
||||
"extract-zip": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
@ -3474,15 +3489,6 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron/node_modules/@types/node": {
|
||||
"version": "18.19.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz",
|
||||
"integrity": "sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
@ -3684,9 +3690,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@ -5005,9 +5011,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
|
||||
"integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==",
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz",
|
||||
"integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Heynote",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.0-beta",
|
||||
"main": "dist-electron/main/index.js",
|
||||
"description": "A dedicated scratch pad",
|
||||
"author": "Jonatan Heyman (https://heyman.info)",
|
||||
@ -44,6 +44,7 @@
|
||||
"@codemirror/lang-python": "^6.1.3",
|
||||
"@codemirror/lang-rust": "^6.0.1",
|
||||
"@codemirror/lang-sql": "^6.5.4",
|
||||
"@codemirror/lang-vue": "^0.1.3",
|
||||
"@codemirror/lang-xml": "^6.0.2",
|
||||
"@codemirror/language": "^6.9.3",
|
||||
"@codemirror/legacy-modes": "^6.3.3",
|
||||
@ -60,13 +61,13 @@
|
||||
"@types/node": "^20.10.5",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"debounce": "^1.2.1",
|
||||
"electron": "^28.0.0",
|
||||
"electron": "^31.1.0",
|
||||
"electron-builder": "^23.6.0",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
"fs-jetpack": "^5.1.0",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier": "^3.3.2",
|
||||
"rollup-plugin-license": "^3.0.1",
|
||||
"sass": "^1.57.1",
|
||||
"typescript": "^4.9.4",
|
||||
|
@ -1,6 +1,6 @@
|
||||
importScripts("guesslang.min.js")
|
||||
|
||||
GUESSLANG_LANGUAGES = ["json","py","html","sql","md","java","php","css","xml","cpp","rs","cs","rb","sh","yaml","toml","go","clj","erl","js","ts","swift","kt","groovy","ps1"]
|
||||
GUESSLANG_LANGUAGES = ["json","py","html","sql","md","java","php","css","xml","cpp","rs","cs","rb","sh","yaml","toml","go","clj","erl","js","ts","swift","kt","groovy","ps1","dart"]
|
||||
|
||||
const guessLang = new self.GuessLang()
|
||||
|
||||
|
11
public/site.webmanifest
Normal file
11
public/site.webmanifest
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Heynote",
|
||||
"short_name": "Heynote",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon.ico",
|
||||
"sizes": "256x256"
|
||||
}
|
||||
],
|
||||
"display": "standalone"
|
||||
}
|
@ -122,6 +122,8 @@
|
||||
:bracketClosing="settings.bracketClosing"
|
||||
:fontFamily="settings.fontFamily"
|
||||
:fontSize="settings.fontSize"
|
||||
:defaultBlockLanguage="settings.defaultBlockLanguage || 'text'"
|
||||
:defaultBlockLanguageAutoDetect="settings.defaultBlockLanguageAutoDetect === undefined ? true : settings.defaultBlockLanguageAutoDetect"
|
||||
class="editor"
|
||||
ref="editor"
|
||||
@openLanguageSelector="openLanguageSelector"
|
||||
|
@ -29,6 +29,8 @@
|
||||
},
|
||||
fontFamily: String,
|
||||
fontSize: Number,
|
||||
defaultBlockLanguage: String,
|
||||
defaultBlockLanguageAutoDetect: Boolean,
|
||||
},
|
||||
|
||||
components: {},
|
||||
@ -78,6 +80,7 @@
|
||||
})
|
||||
window._heynote_editor = this.editor
|
||||
window.document.addEventListener("currenciesLoaded", this.onCurrenciesLoaded)
|
||||
this.editor.setDefaultBlockLanguage(this.defaultBlockLanguage, this.defaultBlockLanguageAutoDetect)
|
||||
|
||||
// set up buffer change listener
|
||||
window.heynote.buffer.onChangeCallback((event, content) => {
|
||||
@ -145,12 +148,18 @@
|
||||
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: {
|
||||
setLanguage(language) {
|
||||
if (language === "auto") {
|
||||
this.editor.setCurrentLanguage("text", true)
|
||||
this.editor.setCurrentLanguage(null, true)
|
||||
} else {
|
||||
this.editor.setCurrentLanguage(language, false)
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
<script>
|
||||
import { LANGUAGES } from '../../editor/languages.js'
|
||||
|
||||
import KeyboardHotkey from "./KeyboardHotkey.vue"
|
||||
import TabListItem from "./TabListItem.vue"
|
||||
import TabContent from "./TabContent.vue"
|
||||
|
||||
const defaultFontFamily = window.heynote.defaultFontFamily
|
||||
const defaultFontSize = window.heynote.defaultFontSize
|
||||
const defaultDefaultBlockLanguage = "text"
|
||||
const defaultDefaultBlockLanguageAutoDetect = true
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@ -39,6 +43,16 @@
|
||||
bufferPath: this.initialSettings.bufferPath,
|
||||
fontFamily: this.initialSettings.fontFamily || defaultFontFamily,
|
||||
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",
|
||||
isWebApp: window.heynote.platform.isWebApp,
|
||||
@ -89,6 +103,8 @@
|
||||
bufferPath: this.bufferPath,
|
||||
fontFamily: this.fontFamily === defaultFontFamily ? undefined : this.fontFamily,
|
||||
fontSize: this.fontSize === defaultFontSize ? undefined : this.fontSize,
|
||||
defaultBlockLanguage: this.defaultBlockLanguage === "text" ? undefined : this.defaultBlockLanguage,
|
||||
defaultBlockLanguageAutoDetect: this.defaultBlockLanguageAutoDetect === true ? undefined : this.defaultBlockLanguageAutoDetect,
|
||||
})
|
||||
if (!this.showInDock) {
|
||||
this.showInMenu = true
|
||||
@ -255,6 +271,25 @@
|
||||
</label>
|
||||
</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"
|
||||
class="language-auto-detect"
|
||||
/>
|
||||
Auto-detection (default: on)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</TabContent>
|
||||
|
||||
<TabContent tab="appearance" :activeTab="activeTab">
|
||||
@ -417,6 +452,7 @@
|
||||
overflow-y: auto
|
||||
select
|
||||
height: 22px
|
||||
margin: 4px 0
|
||||
.row
|
||||
display: flex
|
||||
.entry
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ViewPlugin, EditorView, Decoration, WidgetType, lineNumbers } from "@codemirror/view"
|
||||
import { layer, RectangleMarker } from "@codemirror/view"
|
||||
import { EditorState, RangeSetBuilder, StateField, Facet , StateEffect, RangeSet} from "@codemirror/state";
|
||||
import { syntaxTree, ensureSyntaxTree } from "@codemirror/language"
|
||||
import { syntaxTree, ensureSyntaxTree, syntaxTreeAvailable } from "@codemirror/language"
|
||||
import { Note, Document, NoteDelimiter } from "../lang-heynote/parser.terms.js"
|
||||
import { IterMode } from "@lezer/common";
|
||||
import { heynoteEvent, LANGUAGE_CHANGE } from "../annotation.js";
|
||||
@ -10,12 +10,25 @@ import { mathBlock } from "./math.js"
|
||||
import { emptyBlockSelected } from "./select-all.js";
|
||||
|
||||
|
||||
function startTimer() {
|
||||
const timeStart = performance.now();
|
||||
return function () {
|
||||
return Math.round(performance.now() - timeStart);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// tracks the size of the first delimiter
|
||||
let firstBlockDelimiterSize
|
||||
|
||||
function getBlocks(state, timeout=50) {
|
||||
/**
|
||||
* Return a list of blocks in the document from the syntax tree.
|
||||
* syntaxTreeAvailable() should have been called before this function to ensure the syntax tree is available.
|
||||
*/
|
||||
export function getBlocksFromSyntaxTree(state) {
|
||||
//const timer = startTimer()
|
||||
const blocks = [];
|
||||
const tree = ensureSyntaxTree(state, state.doc.length, timeout)
|
||||
const tree = syntaxTree(state, state.doc.length)
|
||||
if (tree) {
|
||||
tree.iterate({
|
||||
enter: (type) => {
|
||||
@ -52,12 +65,90 @@ function getBlocks(state, timeout=50) {
|
||||
});
|
||||
firstBlockDelimiterSize = blocks[0]?.delimiter.to
|
||||
}
|
||||
//console.log("getBlocksSyntaxTree took", timer(), "ms")
|
||||
return blocks
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse blocks from document's string contents using String.indexOf()
|
||||
*/
|
||||
export function getBlocksFromString(state) {
|
||||
//const timer = startTimer()
|
||||
const blocks = []
|
||||
const doc = state.doc
|
||||
if (doc.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const content = doc.sliceString(0, doc.length)
|
||||
const delim = "\n∞∞∞"
|
||||
let pos = 0
|
||||
while (pos < doc.length) {
|
||||
const blockStart = content.indexOf(delim, pos);
|
||||
if (blockStart != pos) {
|
||||
console.error("Error parsing blocks, expected delimiter at", pos)
|
||||
break;
|
||||
}
|
||||
const langStart = blockStart + delim.length;
|
||||
const delimiterEnd = content.indexOf("\n", langStart)
|
||||
if (delimiterEnd < 0) {
|
||||
console.error("Error parsing blocks. Delimiter didn't end with newline")
|
||||
break
|
||||
}
|
||||
const langFull = content.substring(langStart, delimiterEnd);
|
||||
let auto = false;
|
||||
let lang = langFull;
|
||||
if (langFull.endsWith("-a")) {
|
||||
auto = true;
|
||||
lang = langFull.substring(0, langFull.length - 2);
|
||||
}
|
||||
const contentFrom = delimiterEnd + 1;
|
||||
let blockEnd = content.indexOf(delim, contentFrom);
|
||||
if (blockEnd < 0) {
|
||||
blockEnd = doc.length;
|
||||
}
|
||||
|
||||
const block = {
|
||||
language: {
|
||||
name: lang,
|
||||
auto: auto,
|
||||
},
|
||||
content: {
|
||||
from: contentFrom,
|
||||
to: blockEnd,
|
||||
},
|
||||
delimiter: {
|
||||
from: blockStart,
|
||||
to: delimiterEnd + 1,
|
||||
},
|
||||
range: {
|
||||
from: blockStart,
|
||||
to: blockEnd,
|
||||
},
|
||||
};
|
||||
blocks.push(block);
|
||||
pos = blockEnd;
|
||||
}
|
||||
//console.log("getBlocksFromString() took", timer(), "ms")
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the blocks from the document state.
|
||||
* If the syntax tree is available, we'll extract the blocks from that. Otherwise
|
||||
* the blocks are parsed from the string contents of the document, which is much faster
|
||||
* than waiting for the tree parsing to finish.
|
||||
*/
|
||||
export function getBlocks(state) {
|
||||
if (syntaxTreeAvailable(state, state.doc.length)) {
|
||||
return getBlocksFromSyntaxTree(state)
|
||||
} else {
|
||||
return getBlocksFromString(state)
|
||||
}
|
||||
}
|
||||
|
||||
export const blockState = StateField.define({
|
||||
create(state) {
|
||||
return getBlocks(state, 1000);
|
||||
return getBlocks(state);
|
||||
},
|
||||
update(blocks, transaction) {
|
||||
// if blocks are empty it likely means we didn't get a parsed syntax tree, and then we want to update
|
||||
|
@ -7,7 +7,11 @@ import { selectAll } from "./select-all.js";
|
||||
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)
|
||||
return false
|
||||
|
||||
@ -16,7 +20,7 @@ export const insertNewBlockAtCursor = ({ state, dispatch }) => {
|
||||
if (currentBlock) {
|
||||
delimText = `\n∞∞∞${currentBlock.language.name}${currentBlock.language.auto ? "-a" : ""}\n`
|
||||
} else {
|
||||
delimText = "\n∞∞∞text-a\n"
|
||||
delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
|
||||
}
|
||||
dispatch(state.replaceSelection(delimText),
|
||||
{
|
||||
@ -28,13 +32,12 @@ export const insertNewBlockAtCursor = ({ state, dispatch }) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
export const addNewBlockBeforeCurrent = ({ state, dispatch }) => {
|
||||
console.log("addNewBlockBeforeCurrent")
|
||||
export const addNewBlockBeforeCurrent = (editor) => ({ state, dispatch }) => {
|
||||
if (state.readOnly)
|
||||
return false
|
||||
|
||||
const block = getActiveNoteBlock(state)
|
||||
const delimText = "\n∞∞∞text-a\n"
|
||||
const delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
|
||||
|
||||
dispatch(state.update({
|
||||
changes: {
|
||||
@ -50,12 +53,12 @@ export const addNewBlockBeforeCurrent = ({ state, dispatch }) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
export const addNewBlockAfterCurrent = ({ state, dispatch }) => {
|
||||
export const addNewBlockAfterCurrent = (editor) => ({ state, dispatch }) => {
|
||||
if (state.readOnly)
|
||||
return false
|
||||
|
||||
const block = getActiveNoteBlock(state)
|
||||
const delimText = "\n∞∞∞text-a\n"
|
||||
const delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
|
||||
|
||||
dispatch(state.update({
|
||||
changes: {
|
||||
@ -70,12 +73,12 @@ export const addNewBlockAfterCurrent = ({ state, dispatch }) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
export const addNewBlockBeforeFirst = ({ state, dispatch }) => {
|
||||
export const addNewBlockBeforeFirst = (editor) => ({ state, dispatch }) => {
|
||||
if (state.readOnly)
|
||||
return false
|
||||
|
||||
const block = getFirstNoteBlock(state)
|
||||
const delimText = "\n∞∞∞text-a\n"
|
||||
const delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
|
||||
|
||||
dispatch(state.update({
|
||||
changes: {
|
||||
@ -91,11 +94,11 @@ export const addNewBlockBeforeFirst = ({ state, dispatch }) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
export const addNewBlockAfterLast = ({ state, dispatch }) => {
|
||||
export const addNewBlockAfterLast = (editor) => ({ state, dispatch }) => {
|
||||
if (state.readOnly)
|
||||
return false
|
||||
const block = getLastNoteBlock(state)
|
||||
const delimText = "\n∞∞∞text-a\n"
|
||||
const delimText = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
|
||||
|
||||
dispatch(state.update({
|
||||
changes: {
|
||||
@ -131,6 +134,10 @@ export function changeLanguageTo(state, dispatch, block, language, auto) {
|
||||
|
||||
export function changeCurrentBlockLanguage(state, dispatch, language, auto) {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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 { indentUnit, forceParsing, foldGutter } from "@codemirror/language"
|
||||
import { markdown } from "@codemirror/lang-markdown"
|
||||
@ -59,6 +59,8 @@ export class HeynoteEditor {
|
||||
this.deselectOnCopy = keymap === "emacs"
|
||||
this.emacsMetaKey = emacsMetaKey
|
||||
this.fontTheme = new Compartment
|
||||
this.defaultBlockToken = "text"
|
||||
this.defaultBlockAutoDetect = true
|
||||
|
||||
const state = EditorState.create({
|
||||
doc: content || "",
|
||||
@ -84,7 +86,7 @@ export class HeynoteEditor {
|
||||
}),
|
||||
heynoteLang(),
|
||||
noteBlockExtension(this),
|
||||
languageDetection(() => this.view),
|
||||
languageDetection(() => this),
|
||||
|
||||
// set cursor blink rate to 1 second
|
||||
drawSelection({cursorBlinkRate:1000}),
|
||||
@ -206,6 +208,11 @@ export class HeynoteEditor {
|
||||
})
|
||||
}
|
||||
|
||||
setDefaultBlockLanguage(token, autoDetect) {
|
||||
this.defaultBlockToken = token
|
||||
this.defaultBlockAutoDetect = autoDetect
|
||||
}
|
||||
|
||||
formatCurrentBlock() {
|
||||
formatBlockContent({
|
||||
state: this.view.state,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { keymap } from "@codemirror/view"
|
||||
//import { EditorSelection, EditorState } from "@codemirror/state"
|
||||
import {
|
||||
indentLess, indentMore,
|
||||
indentLess, indentMore, redo,
|
||||
} from "@codemirror/commands"
|
||||
|
||||
import {
|
||||
@ -48,11 +48,11 @@ export function heynoteKeymap(editor) {
|
||||
["Mod-x", cutCommand(editor)],
|
||||
["Tab", indentMore],
|
||||
["Shift-Tab", indentLess],
|
||||
["Alt-Shift-Enter", addNewBlockBeforeFirst],
|
||||
["Mod-Shift-Enter", addNewBlockAfterLast],
|
||||
["Alt-Enter", addNewBlockBeforeCurrent],
|
||||
["Mod-Enter", addNewBlockAfterCurrent],
|
||||
["Mod-Alt-Enter", insertNewBlockAtCursor],
|
||||
["Alt-Shift-Enter", addNewBlockBeforeFirst(editor)],
|
||||
["Mod-Shift-Enter", addNewBlockAfterLast(editor)],
|
||||
["Alt-Enter", addNewBlockBeforeCurrent(editor)],
|
||||
["Mod-Enter", addNewBlockAfterCurrent(editor)],
|
||||
["Mod-Alt-Enter", insertNewBlockAtCursor(editor)],
|
||||
["Mod-a", selectAll],
|
||||
["Alt-ArrowUp", moveLineUp],
|
||||
["Alt-ArrowDown", moveLineDown],
|
||||
@ -61,6 +61,7 @@ export function heynoteKeymap(editor) {
|
||||
["Mod-Alt-ArrowDown", newCursorBelow],
|
||||
["Mod-Alt-ArrowUp", newCursorAbove],
|
||||
["Mod-Shift-k", deleteLine],
|
||||
["Mod-Shift-z", redo],
|
||||
{key:"Mod-ArrowUp", run:gotoPreviousBlock, shift:selectPreviousBlock},
|
||||
{key:"Mod-ArrowDown", run:gotoNextBlock, shift:selectNextBlock},
|
||||
{key:"Ctrl-ArrowUp", run:gotoPreviousParagraph, shift:selectPreviousParagraph},
|
||||
|
@ -11,7 +11,7 @@ NoteDelimiter {
|
||||
|
||||
@tokens {
|
||||
noteDelimiterMark { "∞∞∞" }
|
||||
NoteLanguage { "text" | "math" | "javascript" | "typescript" | "jsx" | "tsx" | "json" | "python" | "html" | "sql" | "markdown" | "java" | "php" | "css" | "xml" | "cpp" | "rust" | "csharp" | "ruby" | "shell" | "yaml" | "golang" | "clojure" | "erlang" | "lezer" | "toml" | "swift" | "kotlin" | "groovy" | "diff" | "powershell" }
|
||||
NoteLanguage { "text" | "math" | "javascript" | "typescript" | "jsx" | "tsx" | "json" | "python" | "html" | "sql" | "markdown" | "java" | "php" | "css" | "xml" | "cpp" | "rust" | "csharp" | "ruby" | "shell" | "yaml" | "golang" | "clojure" | "erlang" | "lezer" | "toml" | "swift" | "kotlin" | "groovy" | "diff" | "powershell" | "vue" | "dart" }
|
||||
Auto { "-a" }
|
||||
noteDelimiterEnter { "\n" }
|
||||
//NoteContent { String }
|
||||
|
@ -22,7 +22,13 @@ export function configureNesting() {
|
||||
if (id == NoteContent) {
|
||||
let noteLang = node.node.parent.firstChild.getChildren(NoteLanguage)[0]
|
||||
let langName = input.read(noteLang?.from, noteLang?.to)
|
||||
//console.log("langName:", langName)
|
||||
|
||||
// if the NoteContent is empty, we don't want to return a parser, since that seems to cause an
|
||||
// error for StreamLanguage parsers when the buffer size is large (e.g >300 kb)
|
||||
if (node.node.from == node.node.to) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (langName in languageMapping && languageMapping[langName] !== null) {
|
||||
//console.log("found parser for language:", langName)
|
||||
return {
|
||||
|
@ -10,7 +10,7 @@ export const parser = LRParser.deserialize({
|
||||
maxTerm: 10,
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 1,
|
||||
tokenData: "-x~RbYZ!Z}!O!`#V#W!k#W#X$X#X#Y$k#Z#[%Z#[#]%|#^#_&`#_#`'|#`#a(f#a#b)O#d#e)}#f#g+i#g#h+x#h#i,b#l#m&S#m#n-a%&x%&y-g~!`OX~~!cP#T#U!f~!kOU~~!nR#`#a!w#d#e#l#g#h#r~!zP#c#d!}~#QP#^#_#T~#WP#i#j#Z~#^P#f#g#a~#dP#X#Y#g~#lOT~~#oP#d#e#g~#uQ#[#]#{#g#h#g~$OP#T#U$R~$UP#f#g#l~$[P#]#^$_~$bP#Y#Z$e~$hP#Y#Z#g~$nP#f#g$q~$tP#`#a$w~$zP#T#U$}~%QP#b#c%T~%WP#Z#[#g~%^Q#c#d$q#f#g%d~%gP#c#d%j~%mP#c#d%p~%sP#j#k%v~%yP#m#n#g~&PP#h#i&S~&VP#a#b&Y~&]P#`#a#g~&cQ#T#U&i#g#h'm~&lP#j#k&o~&rP#T#U&u~&zPT~#g#h&}~'QP#V#W'T~'WP#f#g'Z~'^P#]#^'a~'dP#d#e'g~'jP#h#i#g~'pQ#c#d'v#l#m#g~'yP#b#c#g~(PP#c#d(S~(VP#h#i(Y~(]P#`#a(`~(cP#]#^'v~(iP#X#Y(l~(oP#n#o(r~(uP#X#Y(x~({P#f#g#g~)RP#T#U)U~)XQ#f#g)_#h#i)w~)bP#_#`)e~)hP#W#X)k~)nP#c#d)q~)tP#k#l'v~)zP#[#]#g~*QR#[#]#l#c#d*Z#m#n+V~*^P#k#l*a~*dP#X#Y*g~*jP#f#g*m~*pP#g#h*s~*vP#[#]*y~*|P#X#Y+P~+SP#`#a&Y~+YP#h#i+]~+`P#[#]+c~+fP#c#d'v~+lP#i#j+o~+rQ#U#V%v#g#h'g~+{R#[#]*y#e#f&Y#k#l,U~,XP#]#^,[~,_P#Y#Z'g~,eS#X#Y,q#c#d&S#g#h,w#m#n,}~,tP#l#m'g~,zP#l#m#g~-QP#d#e-T~-WP#X#Y-Z~-^P#g#h&}~-dP#T#U&S~-jP%&x%&y-m~-pP%&x%&y-s~-xOY~",
|
||||
tokenData: ".[~RcYZ!^}!O!c#V#W!n#W#X$[#X#Y$}#Z#[%m#[#]&`#^#_&r#_#`(Y#`#a(r#a#b)[#d#e*Z#f#g+u#g#h,U#h#i,n#j#k-m#l#m&f#m#n-s%&x%&y-y~!cOX~~!fP#T#U!i~!nOU~~!qR#`#a!z#d#e#o#g#h#u~!}P#c#d#Q~#TP#^#_#W~#ZP#i#j#^~#aP#f#g#d~#gP#X#Y#j~#oOT~~#rP#d#e#j~#xQ#[#]$O#g#h#j~$RP#T#U$U~$XP#f#g#o~$_Q#T#U$e#]#^$q~$hP#f#g$k~$nP#h#i#j~$tP#Y#Z$w~$zP#Y#Z#j~%QP#f#g%T~%WP#`#a%Z~%^P#T#U%a~%dP#b#c%g~%jP#Z#[#j~%pQ#c#d%T#f#g%v~%yP#c#d%|~&PP#c#d&S~&VP#j#k&Y~&]P#m#n#j~&cP#h#i&f~&iP#a#b&l~&oP#`#a#j~&uQ#T#U&{#g#h'y~'OP#j#k'R~'UP#T#U'X~'^PT~#g#h'a~'dP#V#W'g~'jP#f#g'm~'pP#]#^'s~'vP#d#e$k~'|Q#c#d(S#l#m#j~(VP#b#c#j~(]P#c#d(`~(cP#h#i(f~(iP#`#a(l~(oP#]#^(S~(uP#X#Y(x~({P#n#o)O~)RP#X#Y)U~)XP#f#g#j~)_P#T#U)b~)eQ#f#g)k#h#i*T~)nP#_#`)q~)tP#W#X)w~)zP#c#d)}~*QP#k#l(S~*WP#[#]#j~*^R#[#]#o#c#d*g#m#n+c~*jP#k#l*m~*pP#X#Y*s~*vP#f#g*y~*|P#g#h+P~+SP#[#]+V~+YP#X#Y+]~+`P#`#a&l~+fP#h#i+i~+lP#[#]+o~+rP#c#d(S~+xP#i#j+{~,OQ#U#V&Y#g#h$k~,XR#[#]+V#e#f&l#k#l,b~,eP#]#^,h~,kP#Y#Z$k~,qS#X#Y,}#c#d&f#g#h-T#m#n-Z~-QP#l#m$k~-WP#l#m#j~-^P#d#e-a~-dP#X#Y-g~-jP#g#h'a~-pP#i#j#d~-vP#T#U&f~-|P%&x%&y.P~.SP%&x%&y.V~.[OY~",
|
||||
tokenizers: [0, noteContent],
|
||||
topRules: {"Document":[0,2]},
|
||||
tokenPrec: 0
|
||||
|
@ -25,7 +25,7 @@ function cancelIdleCallbackCompat(id) {
|
||||
}
|
||||
}
|
||||
|
||||
export function languageDetection(getView) {
|
||||
export function languageDetection(getEditor) {
|
||||
const previousBlockContent = {}
|
||||
let idleCallbackId = null
|
||||
|
||||
@ -35,7 +35,8 @@ export function languageDetection(getView) {
|
||||
if (!event.data.guesslang.language) {
|
||||
return
|
||||
}
|
||||
const view = getView()
|
||||
const editor = getEditor()
|
||||
const view = editor.view
|
||||
const state = view.state
|
||||
const block = getActiveNoteBlock(state)
|
||||
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)
|
||||
if (content === "" && redoDepth(update.state) === 0) {
|
||||
// if content is cleared, set language to plaintext
|
||||
const view = getView()
|
||||
// if content is cleared, set language to default
|
||||
const editor = getEditor()
|
||||
const view = editor.view
|
||||
const block = getActiveNoteBlock(view.state)
|
||||
if (block.language.name !== "text") {
|
||||
changeLanguageTo(view.state, view.dispatch, block, "text", true)
|
||||
if (block.language.name !== editor.defaultBlockToken) {
|
||||
changeLanguageTo(view.state, view.dispatch, block, editor.defaultBlockToken, true)
|
||||
}
|
||||
delete previousBlockContent[idx]
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import { cppLanguage } from "@codemirror/lang-cpp"
|
||||
import { xmlLanguage } from "@codemirror/lang-xml"
|
||||
import { rustLanguage } from "@codemirror/lang-rust"
|
||||
import { csharpLanguage } from "@replit/codemirror-lang-csharp"
|
||||
import { vueLanguage } from "@codemirror/lang-vue";
|
||||
|
||||
import { StreamLanguage } from "@codemirror/language"
|
||||
import { ruby } from "@codemirror/legacy-modes/mode/ruby"
|
||||
@ -22,18 +23,18 @@ import { clojure } from "@codemirror/legacy-modes/mode/clojure"
|
||||
import { erlang } from "@codemirror/legacy-modes/mode/erlang"
|
||||
import { toml } from "@codemirror/legacy-modes/mode/toml"
|
||||
import { swift } from "@codemirror/legacy-modes/mode/swift"
|
||||
import { kotlin } from "@codemirror/legacy-modes/mode/clike"
|
||||
import { kotlin, dart } from "@codemirror/legacy-modes/mode/clike"
|
||||
import { groovy } from "@codemirror/legacy-modes/mode/groovy"
|
||||
import { diff } from "@codemirror/legacy-modes/mode/diff";
|
||||
import { powerShell } from "@codemirror/legacy-modes/mode/powershell";
|
||||
|
||||
import typescriptPlugin from "prettier/plugins/typescript.mjs"
|
||||
import babelPrettierPlugin from "prettier/plugins/babel.mjs"
|
||||
import htmlPrettierPlugin from "prettier/esm/parser-html.mjs"
|
||||
import cssPrettierPlugin from "prettier/esm/parser-postcss.mjs"
|
||||
import markdownPrettierPlugin from "prettier/esm/parser-markdown.mjs"
|
||||
import yamlPrettierPlugin from "prettier/plugins/yaml.mjs"
|
||||
import * as prettierPluginEstree from "prettier/plugins/estree.mjs";
|
||||
import typescriptPlugin from "prettier/plugins/typescript"
|
||||
import babelPrettierPlugin from "prettier/plugins/babel"
|
||||
import htmlPrettierPlugin from "prettier/plugins/html"
|
||||
import cssPrettierPlugin from "prettier/plugins/postcss"
|
||||
import markdownPrettierPlugin from "prettier/plugins/markdown"
|
||||
import yamlPrettierPlugin from "prettier/plugins/yaml"
|
||||
import * as prettierPluginEstree from "prettier/plugins/estree";
|
||||
|
||||
|
||||
class Language {
|
||||
@ -253,6 +254,18 @@ export const LANGUAGES = [
|
||||
parser: StreamLanguage.define(powerShell).parser,
|
||||
guesslang: "ps1",
|
||||
}),
|
||||
new Language({
|
||||
token: "vue",
|
||||
name: "Vue",
|
||||
parser: vueLanguage.parser,
|
||||
guesslang: null,
|
||||
}),
|
||||
new Language({
|
||||
token: "dart",
|
||||
name: "Dart",
|
||||
parser: StreamLanguage.define(dart).parser,
|
||||
guesslang: "dart",
|
||||
}),
|
||||
]
|
||||
|
||||
|
||||
|
@ -114,3 +114,32 @@ const runTest = async (page, key, expectedBlocks) => {
|
||||
await expect(await page.locator("css=.heynote-block-start.first")).toHaveCount(1)
|
||||
}
|
||||
|
||||
|
||||
test("test custom default block language", async ({ page, browserName }) => {
|
||||
heynotePage.setContent(`
|
||||
∞∞∞text
|
||||
Text block`)
|
||||
await page.locator("css=.status-block.settings").click()
|
||||
await page.locator("css=li.tab-editing").click()
|
||||
await page.locator("css=select.block-language").selectOption("Rust")
|
||||
await page.locator("body").press("Escape")
|
||||
await page.locator("body").press((heynotePage.isMac ? "Meta" : "Control") + "+Enter")
|
||||
expect(await heynotePage.getContent()).toBe(`
|
||||
∞∞∞text
|
||||
Text block
|
||||
∞∞∞rust-a
|
||||
`)
|
||||
|
||||
await page.locator("css=.status-block.settings").click()
|
||||
await page.locator("css=li.tab-editing").click()
|
||||
await page.locator("css=input.language-auto-detect").click()
|
||||
await page.locator("body").press("Escape")
|
||||
await page.locator("body").press((heynotePage.isMac ? "Meta" : "Control") + "+Enter")
|
||||
expect(await heynotePage.getContent()).toBe(`
|
||||
∞∞∞text
|
||||
Text block
|
||||
∞∞∞rust-a
|
||||
|
||||
∞∞∞rust
|
||||
`)
|
||||
})
|
||||
|
28
tests/block-parsing.spec.js
Normal file
28
tests/block-parsing.spec.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { expect, test } from "@playwright/test"
|
||||
import { EditorState } from "@codemirror/state"
|
||||
|
||||
import { heynoteLang } from "../src/editor/lang-heynote/heynote.js"
|
||||
import { getBlocksFromSyntaxTree, getBlocksFromString } from "../src/editor/block/block.js"
|
||||
|
||||
test("parse blocks from both syntax tree and string contents", async ({page}) => {
|
||||
const contents = `
|
||||
∞∞∞text
|
||||
Text Block A
|
||||
∞∞∞text-a
|
||||
Text Block B
|
||||
∞∞∞json-a
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
∞∞∞python
|
||||
print("Hello, World!")
|
||||
`
|
||||
const state = EditorState.create({
|
||||
doc: contents,
|
||||
extensions: heynoteLang(),
|
||||
})
|
||||
const treeBlocks = getBlocksFromSyntaxTree(state)
|
||||
const stringBlocks = getBlocksFromString(state)
|
||||
|
||||
expect(treeBlocks).toEqual(stringBlocks)
|
||||
})
|
@ -17,10 +17,8 @@ rmSync('dist-electron', { recursive: true, force: true })
|
||||
const isDevelopment = process.env.NODE_ENV === "development" || !!process.env.VSCODE_DEBUG
|
||||
const isProduction = process.env.NODE_ENV === "production"
|
||||
|
||||
const updateReadmeKeybinds = async () => {
|
||||
const readmePath = path.resolve(__dirname, 'README.md')
|
||||
let readme = fs.readFileSync(readmePath, 'utf-8')
|
||||
const keybindsRegex = /^(### What are the default keyboard shortcuts\?\s*).*?^(```\s+#)/gms
|
||||
const injectKeybindsInDocs = async () => {
|
||||
const keybindsRegex = /^(<!-- keyboard_shortcuts -->\s*).*?^(```\s+#)/gms
|
||||
const shortcuts = `$1**On Mac**
|
||||
|
||||
\`\`\`
|
||||
@ -32,8 +30,10 @@ ${keyHelpStr('darwin')}
|
||||
\`\`\`
|
||||
${keyHelpStr('win32')}
|
||||
$2`
|
||||
readme = readme.replace(keybindsRegex, shortcuts)
|
||||
fs.writeFileSync(readmePath, readme)
|
||||
const docsPath = path.resolve(__dirname, 'docs', 'index.md')
|
||||
let docs = fs.readFileSync(docsPath, 'utf-8')
|
||||
docs = docs.replace(keybindsRegex, shortcuts)
|
||||
fs.writeFileSync(docsPath, docs)
|
||||
}
|
||||
|
||||
const updateGuesslangLanguagesInWebWorker = async () => {
|
||||
@ -56,7 +56,7 @@ export default defineConfig({
|
||||
|
||||
plugins: [
|
||||
vue(),
|
||||
updateReadmeKeybinds(),
|
||||
injectKeybindsInDocs(),
|
||||
updateGuesslangLanguagesInWebWorker(),
|
||||
electron([
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<title>Heynote</title>
|
||||
</head>
|
||||
<body>
|
||||
|
Loading…
Reference in New Issue
Block a user