Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created and updated times #56

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions electron/initial-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,23 @@ if (isWindows || isLinux) {
const keyMaxLength = keyHelp.map(([key, help]) => key.length).reduce((a, b) => Math.max(a, b))
const keyHelpStr = keyHelp.map(([key, help]) => `${key.padEnd(keyMaxLength)} ${help}`).join("\n")

// see src/editor/time.js for templates
const getTime = () => {
// Return time in ISO8601 string YYYY-MM-DDTHH:mm:ssZ
return (new Date()).toISOString().replace(/\.\d+Z/,'Z')
}

export const newCreatedUpdatedTime = () => {
return `-c${getTime()}-u${getTime()}`
}


export const initialContent = `
∞∞∞text
∞∞∞text${newCreatedUpdatedTime()}
Welcome to Heynote! 👋

${keyHelpStr}
∞∞∞math
∞∞∞math${newCreatedUpdatedTime()}
This is a Math block. Here, rows are evaluated as math expressions.

radius = 5
Expand All @@ -40,16 +50,16 @@ time = 3900 seconds to minutes
time * 2

1 EUR in USD
∞∞∞markdown
∞∞∞markdown${newCreatedUpdatedTime()}
In Markdown blocks, lists with [x] and [ ] are rendered as checkboxes:

- [x] Download Heynote
- [ ] Try out Heynote
∞∞∞text-a
∞∞∞text-a${newCreatedUpdatedTime()}
`

export const initialDevContent = initialContent + `
∞∞∞python-a
∞∞∞python-a${newCreatedUpdatedTime()}
# hmm
def my_func():
print("hejsan")
Expand All @@ -60,7 +70,7 @@ import {EditorView, keymap} from "@codemirror/view"
import {javascript} from "@codemirror/lang-javascript"
import {indentWithTab, insertTab, indentLess, indentMore} from "@codemirror/commands"
import {nord} from "./nord.mjs"
∞∞∞javascript-a
∞∞∞javascript-a${newCreatedUpdatedTime()}
let editor = new EditorView({
//extensions: [basicSetup, javascript()],
extensions: [
Expand All @@ -84,7 +94,7 @@ let editor = new EditorView({
],
parent: document.getElementById("editor"),
})
∞∞∞json
∞∞∞json${newCreatedUpdatedTime()}
{
"name": "heynote-codemirror",
"type": "module",
Expand Down Expand Up @@ -112,7 +122,7 @@ let editor = new EditorView({
"typescript": "^4.9.4"
}
}
∞∞∞html
∞∞∞html${newCreatedUpdatedTime()}
<html>
<head>
<title>Test</title>
Expand All @@ -124,9 +134,9 @@ let editor = new EditorView({
</script>
</body>
</html>
∞∞∞sql
∞∞∞sql${newCreatedUpdatedTime()}
SELECT * FROM table WHERE id = 1;
∞∞∞text
∞∞∞text${newCreatedUpdatedTime()}
Shopping list:

- Milk
Expand Down
6 changes: 6 additions & 0 deletions src/components/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
showLanguageSelector: false,
showSettings: false,
settings: window.heynote.settings,
createdTime: "",
updatedTime: "",
}
},

Expand Down Expand Up @@ -84,6 +86,8 @@
this.selectionSize = e.selectionSize
this.language = e.language
this.languageAuto = e.languageAuto
this.createdTime = e.createdTime
this.updatedTime = e.updatedTime
},

openLanguageSelector() {
Expand Down Expand Up @@ -132,6 +136,8 @@
:themeSetting="themeSetting"
:autoUpdate="settings.autoUpdate"
:allowBetaVersions="settings.allowBetaVersions"
:createdTime="createdTime"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't currently show the createdTime, so these props could be removed.

If you want to show the createdTime somewhere, then the prop could be kept

:updatedTime="updatedTime"
@toggleTheme="toggleTheme"
@openLanguageSelector="openLanguageSelector"
@formatCurrentBlock="formatCurrentBlock"
Expand Down
2 changes: 2 additions & 0 deletions src/components/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
selectionSize: e.selectionSize,
language: e.language,
languageAuto: e.languageAuto,
createdTime: e.createdTime,
updatedTime: e.updatedTime,
})
})

Expand Down
15 changes: 15 additions & 0 deletions src/components/StatusBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"themeSetting",
"autoUpdate",
"allowBetaVersions",
"createdTime",
"updatedTime",
],

components: {
Expand Down Expand Up @@ -68,6 +70,11 @@
Sel <span class="num">{{ selectionSize }}</span>
</template>
</div>
<div class="status-block updated-time" v-if="updatedTime !== ''">
Updated <span class="time">{{ updatedTime }}</span>
<!-- Show the created time as well -- too verbose -->
<!-- <span v-if="createdTime !== updatedTime">&nbsp;&nbsp;(Created {{ createdTime }})</span> -->
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looked too busy to me. Perhaps the better UI would be to click on the udpated time to see the created time? That wouldn't be clear either though.

Feel free to adjust this as you see it

</div>
<div class="spacer"></div>
<div
@click="$emit('openLanguageSelector')"
Expand Down Expand Up @@ -140,6 +147,14 @@
color: rgba(255, 255, 255, 0.55)
.num
color: rgba(255, 255, 255, 0.75)
.updated-time
color: rgba(255, 255, 255, 0.7)
.time
color: rgba(255, 255, 255, 1.0)
+dark-mode
color: rgba(255, 255, 255, 0.55)
.time
color: rgba(255, 255, 255, 0.75)
.lang .auto
color: rgba(255, 255, 255, 0.7)
+dark-mode
Expand Down
44 changes: 44 additions & 0 deletions src/editor/block/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { heynoteEvent, LANGUAGE_CHANGE } from "../annotation.js";
import { SelectionChangeEvent } from "../event.js"
import { mathBlock } from "./math.js"
import { emptyBlockSelected } from "./select-all.js";
import { newUpdatedTime, displayTime, timeMatcher } from "../time.js";
import { LANGUAGES } from '../languages.js';

const languageTokensMatcher = LANGUAGES.map(l => l.token).join("|")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to copy this snippet over a few times - perhaps this goes in languges.js and can be imported?


// tracks the size of the first delimiter
let firstBlockDelimiterSize
Expand All @@ -25,12 +28,19 @@ function getBlocks(state, timeout=50) {
const langNode = type.node.getChild("NoteLanguage")
const language = state.doc.sliceString(langNode.from, langNode.to)
const isAuto = !!type.node.getChild("Auto")
const createdAtNode = type.node.getChild("NoteCreated")
const updatedAtNode = type.node.getChild("NoteUpdated")
const contentNode = type.node.nextSibling

blocks.push({
language: {
name: language,
auto: isAuto,
},
time: {
created: createdAtNode ? state.doc.sliceString(createdAtNode.from, createdAtNode.to) : null,
updated: updatedAtNode ? state.doc.sliceString(updatedAtNode.from, updatedAtNode.to) : null,
},
content: {
from: contentNode.from,
to: contentNode.to,
Expand Down Expand Up @@ -326,13 +336,44 @@ const emitCursorChange = (editor) => ViewPlugin.fromClass(
selectionSize,
language: block.language.name,
languageAuto: block.language.auto,
createdTime: displayTime(block.time.created),
updatedTime: displayTime(block.time.updated),
}))
}
}
}
}
)

const updateTimeOnChange = EditorState.transactionFilter.of((tr) => {
if (!tr.docChanged) return tr

const state = tr.startState
const block = getActiveNoteBlock(state)

// Block updates to time when deleting the last content in a block
if ((block.content.from === block.content.to) && !tr.changes.inserted.length) return tr

// this adds a slight debounce so the delimiter is only updated every second
const updatedTime = newUpdatedTime()
if (block.time.updated === updatedTime) return tr

const language = block.language.name
const auto = block.language.auto
const createdTimeStr = block.time.created || ""
const updatedTimeStr = block.time.updated ? updatedTime : ""

// return original transaction, with additional transaction to update time in delimiter
return [tr, {
changes: {
from: block.delimiter.from,
to: block.delimiter.to,
insert: `\n∞∞∞${language}${auto ? '-a' : ''}${createdTimeStr}${updatedTimeStr}\n`,
},
filter: false
}]
})

export const noteBlockExtension = (editor) => {
return [
blockState,
Expand All @@ -344,5 +385,8 @@ export const noteBlockExtension = (editor) => {
emitCursorChange(editor),
mathBlock,
emptyBlockSelected,
updateTimeOnChange,
]
}


21 changes: 13 additions & 8 deletions src/editor/block/commands.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { EditorSelection } from "@codemirror/state"
import { LANGUAGES } from '../languages.js';
import { heynoteEvent, LANGUAGE_CHANGE, CURRENCIES_LOADED } from "../annotation.js";
import { blockState, getActiveNoteBlock, getNoteBlockFromPos } from "./block"
import { blockState, getActiveNoteBlock, getNoteBlockFromPos} from "./block"
import { moveLineDown, moveLineUp } from "./move-lines.js";
import { selectAll } from "./select-all.js";
import { newCreatedUpdatedTime, newUpdatedTime, timeMatcher } from "../time.js";

export { moveLineDown, moveLineUp, selectAll }
const languageTokensMatcher = LANGUAGES.map(l => l.token).join("|")

export { moveLineDown, moveLineUp, selectAll }

export const insertNewBlockAtCursor = ({ state, dispatch }) => {
if (state.readOnly)
Expand All @@ -14,9 +17,9 @@ export const insertNewBlockAtCursor = ({ state, dispatch }) => {
const currentBlock = getActiveNoteBlock(state)
let delimText;
if (currentBlock) {
delimText = `\n∞∞∞${currentBlock.language.name}${currentBlock.language.auto ? "-a" : ""}\n`
delimText = `\n∞∞∞${currentBlock.language.name}${currentBlock.language.auto ? "-a" : ""}${newCreatedUpdatedTime()}\n`
} else {
delimText = "\n∞∞∞text-a\n"
delimText = `\n∞∞∞text-a${newCreatedUpdatedTime()}\n`
}
dispatch(state.replaceSelection(delimText),
{
Expand All @@ -32,7 +35,7 @@ export const addNewBlockAfterCurrent = ({ state, dispatch }) => {
if (state.readOnly)
return false
const block = getActiveNoteBlock(state)
const delimText = "\n∞∞∞text-a\n"
const delimText = `\n∞∞∞text-a${newCreatedUpdatedTime()}\n`

dispatch(state.update({
changes: {
Expand All @@ -50,14 +53,16 @@ export const addNewBlockAfterCurrent = ({ state, dispatch }) => {
export function changeLanguageTo(state, dispatch, block, language, auto) {
if (state.readOnly)
return false
const delimRegex = /^\n∞∞∞[a-z]{0,16}(-a)?\n/g
const delimRegex = new RegExp(`\\n∞∞∞(${languageTokensMatcher})(-a)?(-c${timeMatcher})?(-u${timeMatcher})?\\n`, "g")
if (state.doc.sliceString(block.delimiter.from, block.delimiter.to).match(delimRegex)) {
//console.log("changing language to", language)
const createdTimeStr = block.time.created || ""
const updatedTimeStr = block.time.updated ? newUpdatedTime() : ""
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't explicitly needed - this will get updated by the transactionFilter, but it doesn't hurt to add it since the other function has the transactionFilter has the debounce

// console.log("changing language to", language)
dispatch(state.update({
changes: {
from: block.delimiter.from,
to: block.delimiter.to,
insert: `\n∞∞∞${language}${auto ? '-a' : ''}\n`,
insert: `\n∞∞∞${language}${auto ? '-a' : ''}${createdTimeStr}${updatedTimeStr}\n`,
},
annotations: [heynoteEvent.of(LANGUAGE_CHANGE)],
}))
Expand Down
3 changes: 2 additions & 1 deletion src/editor/block/move-lines.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { EditorSelection } from "@codemirror/state"
import { blockState } from "./block"
import { LANGUAGES } from '../languages.js';
import { timeMatcher } from '../time.js';

const languageTokensMatcher = LANGUAGES.map(l => l.token).join("|")
const tokenRegEx = new RegExp(`^∞∞∞(${languageTokensMatcher})(-a)?$`, "g")
const tokenRegEx = new RegExp(`^∞∞∞(${languageTokensMatcher})(-a)?(-c${timeMatcher})?(-u${timeMatcher})?$`, "g")


function selectedLineBlocks(state) {
Expand Down
3 changes: 2 additions & 1 deletion src/editor/copy-paste.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { EditorState, EditorSelection } from "@codemirror/state"
import { EditorView } from "@codemirror/view"

import { LANGUAGES } from './languages.js';
import { timeMatcher } from './time.js';
import { setEmacsMarkMode } from "./emacs.js"


const languageTokensMatcher = LANGUAGES.map(l => l.token).join("|")
const blockSeparatorRegex = new RegExp(`\\n∞∞∞(${languageTokensMatcher})(-a)?\\n`, "g")
const blockSeparatorRegex = new RegExp(`\\n∞∞∞(${languageTokensMatcher})(-a)?(-c${timeMatcher})?(-u${timeMatcher})?\\n`, "g")


function copiedRange(state) {
Expand Down
4 changes: 3 additions & 1 deletion src/editor/event.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export class SelectionChangeEvent extends Event {
constructor({cursorLine, language, languageAuto, selectionSize}) {
constructor({cursorLine, language, languageAuto, selectionSize, createdTime, updatedTime}) {
super("selectionChange")
this.cursorLine = cursorLine
this.selectionSize = selectionSize
this.language = language
this.languageAuto = languageAuto
this.createdTime = createdTime
this.updatedTime = updatedTime
}
}
5 changes: 3 additions & 2 deletions src/editor/lang-heynote/external-tokens.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ExternalTokenizer } from '@lezer/lr'
import { NoteContent } from "./parser.terms.js"
import { LANGUAGES } from '../languages.js';
import { timeMatcher } from '../time.js';

const EOF = -1;

const FIRST_TOKEN_CHAR = "\n".charCodeAt(0)
const SECOND_TOKEN_CHAR = "∞".charCodeAt(0)

const languageTokensMatcher = LANGUAGES.map(l => l.token).join("|")
const tokenRegEx = new RegExp(`^\\n∞∞∞(${languageTokensMatcher})(-a)?\\n`, "g")
const tokenRegEx = new RegExp(`^\\n∞∞∞(${languageTokensMatcher})(-a)?(-c${timeMatcher})?(-u${timeMatcher})?\\n`, "g")

export const noteContent = new ExternalTokenizer((input) => {
let current = input.peek(0);
Expand All @@ -23,7 +24,7 @@ export const noteContent = new ExternalTokenizer((input) => {
// so we don't need to check for the rest of the token
if (current === FIRST_TOKEN_CHAR && next === SECOND_TOKEN_CHAR) {
let potentialLang = "";
for (let i=0; i<18; i++) {
for (let i=0; i<62; i++) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note I added the 40 characters of the times to this.

potentialLang += String.fromCharCode(input.peek(i));
}
if (potentialLang.match(tokenRegEx)) {
Expand Down
15 changes: 14 additions & 1 deletion src/editor/lang-heynote/heynote.grammar
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,28 @@ Note {
}

NoteDelimiter {
noteDelimiterEnter noteDelimiterMark NoteLanguage Auto? noteDelimiterEnter
noteDelimiterEnter noteDelimiterMark NoteLanguage Auto? NoteCreated? NoteUpdated? noteDelimiterEnter
}

NoteCreated {
noteCreatedDelimiter time
}

NoteUpdated {
noteUpdatedDelimiter time
}

@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" }
Auto { "-a" }
noteDelimiterEnter { "\n" }
noteCreatedDelimiter { "-c" }
noteUpdatedDelimiter { "-u" }
time {
@digit @digit @digit @digit "-" @digit @digit "-" @digit @digit "T"
@digit @digit ":" @digit @digit ":" @digit @digit "Z"
}
//NoteContent { String }
//String { (![∞])+ }
}
Expand Down
Loading
Loading