Skip to content

Commit

Permalink
feat: basic in-development comment gutter
Browse files Browse the repository at this point in the history
  • Loading branch information
Fevol committed Aug 4, 2023
1 parent af2e03a commit 1a9cd34
Show file tree
Hide file tree
Showing 5 changed files with 718 additions and 21 deletions.
51 changes: 48 additions & 3 deletions src/assets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,18 @@
padding: 0 0 var(--size-4-2) 0;
}

.criticmarkup-comment-icon:hover {
cursor: pointer;
}

.criticmarkup-comment-icon svg {
color: var(--comment-border-color);
stroke-width: 4px;
transition: stroke-width 150ms ease-in-out;
}

.criticmarkup-comment-icon:hover svg {
stroke-width: 5px;
}

.criticmarkup-comment-tooltip {
Expand All @@ -78,11 +87,11 @@
border-radius: var(--radius-l);
}

.criticmarkup-comment-tooltip p {
display: inline;
.criticmarkup-comment-tooltip * {
margin-block-start: 0.25em;
margin-block-end: 0.25em;
}


.criticmarkup-highlight {
background-color: var(--highlight-color);
}
Expand Down Expand Up @@ -164,6 +173,42 @@



.criticmarkup-comment-gutter {
width: 300px;
margin-top: 50px;
}

.criticmarkup-gutter-comment {
color: var(--text-muted);
border: 2px solid var(--background-modifier-border);
border-radius: var(--radius-l);
padding: var(--size-4-2);
transition: border-color 200ms ease-in-out;
background-color: var(--background-secondary-alt);

// Position comments relative to their optical center
//transform: translateY(-25%);

max-height: 150px;
overflow-y: scroll;
}


//.criticmarkup-comment-gutter .cm-gutterElement {
// margin-bottom: 10px;
//}

.criticmarkup-gutter-comment * {
margin-block-start: 0.25em;
margin-block-end: 0.25em;
}


.criticmarkup-gutter-comment:focus {
border: 2px solid var(--comment-border-color);
background-color: var(--background-primary);
}


// ================================================
// Markdown-specific Fixes
Expand Down
119 changes: 119 additions & 0 deletions src/editor/renderers/comment-gutter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { RangeSet, RangeSetBuilder, StateField } from '@codemirror/state';
import { EditorView, GutterMarker } from '@codemirror/view';
import { NodeType, PluginSettings } from '../../types';

import { treeParser } from '../tree-parser';

import { nodesInSelection } from '../editor-util';
import { CriticMarkupNode, CriticMarkupNodes } from '../criticmarkup-nodes';
import { right_gutter } from './right-gutter';
import { Component, editorEditorField, MarkdownRenderer } from 'obsidian';


// TODO: Rerender gutter on Ctrl+Scroll

export class CriticMarkupMarker extends GutterMarker {
node: CriticMarkupNode;
comment: HTMLElement | null = null;
view: EditorView;

constructor(node: CriticMarkupNode, view: EditorView) {
super();
this.node = node;
this.view = view;
}

toDOM() {
let class_list = '';

this.comment = createDiv({ cls: class_list });
this.comment.contentEditable = 'true';

const component = new Component();
this.comment.classList.add('criticmarkup-gutter-comment');
const contents = this.view.state.doc.sliceString(this.node.from + 3, this.node.to - 3);
MarkdownRenderer.renderMarkdown(contents, this.comment, '', component);

this.comment.onblur = () => {
setTimeout(() => this.view.dispatch({
changes: {
from: this.node.from + 3,
to: this.node.to - 3,
insert: this.comment!.innerText
}
}));
}
this.comment.onfocus = (e) => {
this.comment!.innerText = contents;

const top = this.view.lineBlockAt(this.node.from).top;
this.view.scrollDOM.scrollTo({ top, behavior: 'smooth'});


// TODO: Call a function inside the gutter to trigger movement of markers

// Get a reference to the Gutter extension
// @ts-ignore
// console.log(this.view.plugin(commentGutterExtension))
// const gutter = ;


}


component.load();

return this.comment;
}

focus() {
this.comment!.focus();
}
}

export const commentGutterWidgets = StateField.define<RangeSet<CriticMarkupMarker>>({
create() {
return RangeSet.empty;
},
update(oldSet, tr) {
if (!tr.docChanged && oldSet.size)
return oldSet;

const tree = tr.state.field(treeParser).tree;
const builder = new RangeSetBuilder<CriticMarkupMarker>();
const nodes: CriticMarkupNodes = nodesInSelection(tree);
const view = tr.state.field(editorEditorField);

for (const node of nodes.nodes) {
if (node.type !== NodeType.COMMENT) continue;

const line_from = tr.state.doc.lineAt(node.from);
builder.add(line_from.from, line_from.to, new CriticMarkupMarker(node, view));
}

return builder.finish();
},

// provide(field: StateField<CriticMarkupMarker>): Extension {
// return (field);
// }
});

export const commentGutterExtension = /*(settings: PluginSettings) =>*/ [
commentGutterWidgets,
right_gutter({
class: 'criticmarkup-comment-gutter',
markers: v => v.state.field(commentGutterWidgets),
}),
];


// export const commentGutterExtension = (settings: PluginSettings) => right_gutter({
// class: 'criticmarkup-comment-gutter',
// markers(view: EditorView) {
// return buildMarkers(view, view.state.field(treeParser).tree) ?? RangeSet.empty;
// },
// doX() {
// console.log("xxx")
// }
// });
56 changes: 39 additions & 17 deletions src/editor/renderers/live-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@ import {
WidgetType,
} from '@codemirror/view';

import { Component, editorLivePreviewField, MarkdownRenderer, setIcon } from 'obsidian';
import {
Component,
editorLivePreviewField,
MarkdownRenderer,
setIcon,
} from 'obsidian';

import type { PluginSettings } from '../../types';
import { NodeType } from '../../types';
import { nodesInSelection, selectionRangeOverlap } from '../editor-util';
import { CriticMarkupNode, SubstitutionNode } from '../criticmarkup-nodes';
import { commentGutterWidgets } from './comment-gutter';


export const inlineCommentRenderer = StateField.define<DecorationSet>({
export const inlineCommentRenderer = (settings: PluginSettings) => StateField.define<DecorationSet>({
create(state): DecorationSet {
return Decoration.none;
},
Expand Down Expand Up @@ -50,7 +57,7 @@ export const inlineCommentRenderer = StateField.define<DecorationSet>({
node.from,
node.to,
Decoration.replace({
widget: new CommentIconWidget(tr.state.sliceDoc(node.from + 3, node.to - 3)),
widget: new CommentIconWidget(node, tr.state.sliceDoc(node.from + 3, node.to - 3), settings.comment_style === "block"),
})
);
}
Expand All @@ -71,13 +78,17 @@ class CommentIconWidget extends WidgetType {
tooltip: HTMLElement | null = null;
icon: HTMLElement | null = null;

node: CriticMarkupNode;

component: Component;
focused = false;
is_block = false;


constructor(contents: string) {
constructor(node: CriticMarkupNode, contents: string, is_block = false) {
super();
this.node = node;
this.contents = contents;
this.is_block = is_block;
this.component = new Component();
}

Expand Down Expand Up @@ -110,16 +121,29 @@ class CommentIconWidget extends WidgetType {
this.icon.classList.add('criticmarkup-comment-icon');
setIcon(this.icon, 'message-square');

this.icon.onmouseenter = () => { this.renderTooltip(); }
this.icon.onclick = () => {
this.renderTooltip();
this.focused = true;
}
if (this.is_block) {
this.icon.onclick = (e) => {
const gutterElements = view.state.field(commentGutterWidgets);
gutterElements.between(this.node.from, this.node.to, (from, to, widget) => {
widget.focus();
});
};
} else {
if (this.contents.length) {
this.icon.onmouseenter = () => {
this.renderTooltip();
}
this.icon.onclick = () => {
this.renderTooltip();
this.focused = true;
}

this.icon.onmouseleave = () => {
this.unrenderTooltip();
// TODO: Find a better way to check if the tooltip is still focused (requires a document.click listener -> expensive?); .onblur does not work
this.focused = false;
this.icon.onmouseleave = () => {
this.unrenderTooltip();
// TODO: Find a better way to check if the tooltip is still focused (requires a document.click listener -> expensive?); .onblur does not work
this.focused = false;
}
}
}

// this.icon.onblur = () => {
Expand Down Expand Up @@ -198,8 +222,6 @@ function markContents(decorations: Range<Decoration>[], node: CriticMarkupNode,
}
}



export const livePreviewRenderer = (settings: PluginSettings) => StateField.define<DecorationSet>({
create(state): DecorationSet {
return Decoration.none;
Expand All @@ -218,7 +240,7 @@ export const livePreviewRenderer = (settings: PluginSettings) => StateField.defi

for (const node of nodes.nodes) {
if (!settings.preview_mode) {
if (tr.selection?.ranges?.some(range => node.partially_in_range(range.from, range.to))) {
if (!settings.suggest_mode && tr.selection?.ranges?.some(range => node.partially_in_range(range.from, range.to))) {
markContents(decorations, node, 'criticmarkup-editing');
} else if (node.type === NodeType.SUBSTITUTION) {
removeBracket(decorations, node, true);
Expand Down
Loading

0 comments on commit 1a9cd34

Please sign in to comment.