Skip to content

Commit

Permalink
Add hanging indent for lists in the note editor
Browse files Browse the repository at this point in the history
Fixes #347

Implement hanging indent for lists in the note editor to improve readability.

* **Add `indentedLineWrap` plugin:**
  - Import `indentedLineWrap` in `src/components/note-editor.tsx`.
  - Add `indentedLineWrap` to the `extensions` array in `NoteEditor`.

* **Add CSS styles for indented wrapped lines:**
  - Add styles for `.indented-wrapped-line` in `src/styles/codemirror.css`.
  - Include margin-left and text-indent properties.
  - Add styles for active line and indent markers.

* **Create `indentedLineWrap` plugin:**
  - Add new file `src/utils/codemirror/indentedLineWrap.ts`.
  - Implement `indentedLineWrap` plugin to handle list indentation.
  - Define `getStartTabs` and `getDecorations` functions.
  - Use `StateField` to define the plugin.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace-dev.githubnext.com/lumen-notes/lumen/issues/347?shareId=XXXX-XXXX-XXXX-XXXX).
  • Loading branch information
colebemis committed Aug 21, 2024
1 parent a6b74c4 commit 9b9fe05
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/components/note-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { removeLeadingEmoji } from "../utils/emoji"
import { parseFrontmatter } from "../utils/parse-frontmatter"
import { removeParentTags } from "../utils/remove-parent-tags"
import { useInsertTemplate } from "./insert-template"
import { indentedLineWrap } from "../utils/codemirror/indentedLineWrap"

type NoteEditorProps = {
className?: string
Expand Down Expand Up @@ -128,6 +129,7 @@ export const NoteEditor = React.forwardRef<ReactCodeMirrorRef, NoteEditorProps>(
spellcheckExtension(),
pasteExtension({ attachFile, onPaste }),
syntaxHighlighting(syntaxHighlighter),
indentedLineWrap,
]

if (editorSettings.vimMode) {
Expand Down
13 changes: 13 additions & 0 deletions src/styles/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,16 @@
.cm-editor .cm-lineNumbers .cm-gutterElement {
@apply min-w-[1.5rem] p-0;
}

.indented-wrapped-line {
margin-left: calc(var(--indented));
text-indent: calc(-1 * var(--indented));
}

.indented-wrapped-line.cm-activeLine {
box-shadow: calc((-1 * var(--indented)) + 4px) 0 0 rgba(255, 255, 255, 0.025);
}

.indented-wrapped-line.cm-indent-markers::before {
left: calc(var(--indented) * -1);
}
44 changes: 44 additions & 0 deletions src/utils/codemirror/indentedLineWrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { EditorView, Decoration } from "@codemirror/view";
import { StateField, EditorState } from "@codemirror/state";

export const getStartTabs = (line: string): string => /^\t*/.exec(line)?.[0] ?? "";

const getDecorations = (state: EditorState) => {
const decorations: Decoration[] = [];

for (let i = 0; i < state.doc.lines; i++) {
const line = state.doc.line(i + 1);
const numberOfTabs = getStartTabs(line.text).length;
if (numberOfTabs === 0) continue;

const offset = numberOfTabs * state.tabSize;

const linerwapper = Decoration.line({
attributes: {
style: `--indented: ${offset}ch;`,
class: "indented-wrapped-line",
},
});

decorations.push(linerwapper.range(line.from, line.from));

Check failure on line 23 in src/utils/codemirror/indentedLineWrap.ts

View workflow job for this annotation

GitHub Actions / build

Argument of type 'Range<Decoration>' is not assignable to parameter of type 'Decoration'.
}

return Decoration.set(decorations);

Check failure on line 26 in src/utils/codemirror/indentedLineWrap.ts

View workflow job for this annotation

GitHub Actions / build

Argument of type 'Decoration[]' is not assignable to parameter of type 'Range<Decoration> | readonly Range<Decoration>[]'.
};

/**
* Plugin that makes line wrapping in the editor respect the identation of the line.
* It does this by adding a line decoration that adds margin-left (as much as there is indentation),
* and adds the same amount as negative "text-indent". The nice thing about text-indent is that it
* applies to the initial line of a wrapped line.
*/
export const indentedLineWrap = StateField.define<Decoration[]>({
create(state) {

Check failure on line 36 in src/utils/codemirror/indentedLineWrap.ts

View workflow job for this annotation

GitHub Actions / build

Type '(state: EditorState) => DecorationSet' is not assignable to type '(state: EditorState) => Decoration[]'.
return getDecorations(state);
},
update(deco, tr) {

Check failure on line 39 in src/utils/codemirror/indentedLineWrap.ts

View workflow job for this annotation

GitHub Actions / build

Type '(deco: Decoration[], tr: Transaction) => Decoration[] | DecorationSet' is not assignable to type '(value: Decoration[], transaction: Transaction) => Decoration[]'.
if (!tr.docChanged) return deco;
return getDecorations(tr.state);
},
provide: (f) => EditorView.decorations.from(f),

Check failure on line 43 in src/utils/codemirror/indentedLineWrap.ts

View workflow job for this annotation

GitHub Actions / build

Argument of type 'StateField<Decoration[]>' is not assignable to parameter of type 'StateField<DecorationSet | ((view: EditorView) => DecorationSet)>'.
});

0 comments on commit 9b9fe05

Please sign in to comment.