Skip to content

Commit

Permalink
Integrate markedit-api package
Browse files Browse the repository at this point in the history
  • Loading branch information
cyanzhong committed Sep 16, 2024
1 parent 65495df commit fb4ef23
Show file tree
Hide file tree
Showing 18 changed files with 326 additions and 8 deletions.
1 change: 1 addition & 0 deletions CoreEditor/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
}
</style>
<script>
MarkEdit = {};
window.config = "{{EDITOR_CONFIG}}";
</script>
</head>
Expand Down
4 changes: 4 additions & 0 deletions CoreEditor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ import { NativeModulePreview } from './src/bridge/native/preview';
import { NativeModuleTokenizer } from './src/bridge/native/tokenizer';

import { resetEditor } from './src/core';
import { initMarkEditModules } from './src/api/modules';
import { setUp } from './src/styling/config';
import { loadTheme } from './src/styling/themes';
import { startObserving } from './src/events';

// Initialize and inject modules to the global MarkEdit object
initMarkEditModules();

// In release mode, window.config = "{{EDITOR_CONFIG}}" will be replaced with a JSON literal
const config = isReleaseMode ? window.config : {
text: pseudoDocument,
Expand Down
3 changes: 2 additions & 1 deletion CoreEditor/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "mark-edit",
"name": "markedit-app",
"version": "1.0.0",
"description": "Just like TextEdit on Mac but dedicated to Markdown.",
"scripts": {
Expand Down Expand Up @@ -35,6 +35,7 @@
"eslint-plugin-promise": "^6.6.0",
"jest": "^29.6.4",
"jest-environment-jsdom": "^29.6.4",
"markedit-api": "https://github.com/MarkEdit-app/MarkEdit-api#v0.4.0",
"rollup": "^4.0.0",
"ts-gyb": "^0.12.0",
"ts-jest": "^29.1.1",
Expand Down
15 changes: 15 additions & 0 deletions CoreEditor/src/@types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { NativeModuleCore } from '../bridge/native/core';
import { NativeModuleCompletion } from '../bridge/native/completion';
import { NativeModulePreview } from '../bridge/native/preview';
import { NativeModuleTokenizer } from '../bridge/native/tokenizer';
import { TextEditor } from '../api/editor';

import type { Extension } from '@codemirror/state';
import type { MarkdownConfig } from '@lezer/markdown';

declare global {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -23,6 +27,17 @@ interface WebKit {
}

declare global {
// https://github.com/MarkEdit-app/MarkEdit-api
const MarkEdit: {
editorView: EditorView;
editorAPI: TextEditor;
codemirror: { view: Module; state: Module; language: Module; commands: Module; search: Module };
lezer: { common: Module; highlight: Module; markdown: Module; lr: Module };
onEditorReady: (listener: (editorView: EditorView) => void) => void;
addExtension: (extension: Extension) => void;
addMarkdownConfig: (config: MarkdownConfig) => void;
};

interface Window {
webkit?: WebKit;
editor: EditorView;
Expand Down
83 changes: 83 additions & 0 deletions CoreEditor/src/api/editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { EditorView } from '@codemirror/view';
import { EditorSelection, EditorState, Text } from '@codemirror/state';
import { syntaxTree } from '@codemirror/language';
import { TextEditable, TextRange } from 'markedit-api';
import { redo, undo } from '../@vendor/commands/history';

/**
* TextEditable implementation to provide convenient text editing interfaces.
*/
export class TextEditor implements TextEditable {
setView(view: EditorView) {
this.view = view;
}

getText(range?: TextRange): string {
if (range === undefined) {
return this.doc.toString();
}

const { from, to } = range;
return this.doc.sliceString(from, to);
}

setText(text: string, range?: TextRange): void {
const from = range === undefined ? 0 : range.from;
const to = range === undefined ? this.doc.length : range.to;
this.view.dispatch({
changes: { from, to, insert: text },
});
}

getSelections(): TextRange[] {
return this.state.selection.ranges.map(({ from, to }) => ({ from, to }));
}

setSelections(ranges: TextRange[]): void {
const selections = ranges.map(({ from, to }) => EditorSelection.range(from, to));
this.view.dispatch({
selection: EditorSelection.create(selections),
});
}

getLineNumber(position: number): number {
return this.doc.lineAt(position).number - 1;
}

getLineRange(row: number): TextRange {
const { from, to } = this.doc.line(row + 1);
return { from, to };
}

getLineCount(): number {
return this.doc.lines;
}

getLineBreak(): string {
return this.state.lineBreak;
}

getNodeName(position: number): string {
return syntaxTree(this.state).resolve(position).name;
}

undo(): void {
undo(this.view);
}

redo(): void {
redo(this.view);
}

// MARK: - Private

private view = window.editor;

private get state(): EditorState {
return this.view.state;
}

private get doc(): Text {
return this.state.doc;
}
}
58 changes: 58 additions & 0 deletions CoreEditor/src/api/methods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { EditorView } from '@codemirror/view';
import { Extension } from '@codemirror/state';
import { MarkdownConfig } from '@lezer/markdown';
import { markdownExtensionBundle } from '../extensions';

export function onEditorReady(listener: (editorView: EditorView) => void) {
storage.editorReadyListeners.push(listener);

if (isEditorReady()) {
listener(window.editor);
}
}

export function addExtension(extension: Extension) {
storage.extensions.push(extension);

if (isEditorReady()) {
window.editor.dispatch({
effects: window.dynamics.extensionConfigurator?.reconfigure(userExtensions()),
});
}
}

export function addMarkdownConfig(config: MarkdownConfig) {
storage.markdownConfigs.push(config);

if (isEditorReady()) {
window.editor.dispatch({
effects: window.dynamics.markdownConfigurator?.reconfigure(markdownExtensionBundle()),
});
}
}

export function editorReadyListeners() {
return storage.editorReadyListeners;
}

export function userExtensions(): Extension[] {
return storage.extensions;
}

export function userMarkdownConfigs(): MarkdownConfig[] {
return storage.markdownConfigs;
}

function isEditorReady() {
return typeof window.editor.dispatch === 'function';
}

const storage: {
editorReadyListeners: ((editorView: EditorView) => void)[];
extensions: Extension[];
markdownConfigs: MarkdownConfig[];
} = {
editorReadyListeners: [],
extensions: [],
markdownConfigs: [],
};
69 changes: 69 additions & 0 deletions CoreEditor/src/api/modules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as cmView from '@codemirror/view';
import * as cmState from '@codemirror/state';
import * as cmLanguage from '@codemirror/language';
import * as cmCommands from '@codemirror/commands';
import * as cmSearch from '@codemirror/search';

import * as lezerCommon from '@lezer/common';
import * as lezerHighlight from '@lezer/highlight';
import * as lezerMarkdown from '@lezer/markdown';
import * as lezerLr from '@lezer/lr';

import * as customHistory from '../@vendor/commands/history';

import { TextEditor } from './editor';
import { onEditorReady, addExtension, addMarkdownConfig } from './methods';

export function initMarkEditModules() {
const codemirror = {
view: cmView,
state: cmState,
language: cmLanguage,
commands: {
...cmCommands,
...customHistory,
},
search: cmSearch,
};

const lezer = {
common: lezerCommon,
highlight: lezerHighlight,
markdown: lezerMarkdown,
lr: lezerLr,
};

MarkEdit.editorAPI = new TextEditor();
MarkEdit.codemirror = codemirror;
MarkEdit.lezer = lezer;

MarkEdit.onEditorReady = onEditorReady;
MarkEdit.addExtension = addExtension;
MarkEdit.addMarkdownConfig = addMarkdownConfig;

const modules = {
'markedit-api': { MarkEdit },
'@codemirror/view': codemirror.view,
'@codemirror/state': codemirror.state,
'@codemirror/language': codemirror.language,
'@codemirror/commands': codemirror.commands,
'@codemirror/search': codemirror.search,
'@lezer/common': lezer.common,
'@lezer/highlight': lezer.highlight,
'@lezer/markdown': lezer.markdown,
'@lezer/lr': lezer.lr,
};

const require = (id: string) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const module = (modules as any)[id];
if (module !== undefined) {
return module;
}

console.error(`Failed to require module: "${id}", supported modules: ${Object.keys(modules).join(', ')}`);
return {};
};

window.require = require as NodeRequire;
}
1 change: 1 addition & 0 deletions CoreEditor/src/bridge/native/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface NativeModuleCore extends NativeModule {
notifyBackgroundColorDidChange({ color }: { color: CodeGen_Int }): void;
notifyViewportScaleDidChange(): void;
notifyViewDidUpdate(args: { contentEdited: boolean; compositionEnded: boolean; isDirty: boolean; selectedLineColumn: LineColumnInfo }): void;
notifyContentHeightDidChange({ bottomPanelHeight }: { bottomPanelHeight: number }): void;
notifyContentOffsetDidChange(): void;
notifyCompositionEnded({ selectedLineColumn }: { selectedLineColumn: LineColumnInfo }): void;
notifyLinkClicked({ link }: { link: string }): void;
Expand Down
2 changes: 2 additions & 0 deletions CoreEditor/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ export interface Dynamics {
lineEndings?: Compartment;
indentUnit?: Compartment;
selectionHighlight?: Compartment;
extensionConfigurator?: Compartment;
markdownConfigurator?: Compartment;
}

export type { WebFontFace };
31 changes: 31 additions & 0 deletions CoreEditor/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { getLineBreak, normalizeLineBreaks } from './modules/lineEndings';
import { generateDiffs } from './modules/diff';
import { scrollCaretToVisible, scrollIntoView } from './modules/selection';
import { markContentClean } from './modules/history';
import { editorReadyListeners } from './api/methods';

export enum ReplaceGranularity {
wholeDocument = 'wholeDocument',
Expand Down Expand Up @@ -62,6 +63,9 @@ export function resetEditor(
editor.focus();
window.editor = editor;

MarkEdit.editorView = editor;
MarkEdit.editorAPI.setView(editor);

const ensureLineHeight = () => {
// coordsAtPos ensures the line number height
scrollCaretToVisible();
Expand All @@ -80,6 +84,8 @@ export function resetEditor(

const scrollDOM = editor.scrollDOM;
scrollDOM.scrollTo({ top: 0 }); // scrollIntoView doesn't work when the app is idle

observeContentHeightChanges(scrollDOM);
fixWebKitWheelIssues(scrollDOM);

scrollDOM.addEventListener('scroll', () => {
Expand Down Expand Up @@ -123,6 +129,9 @@ export function resetEditor(

// The content should be initially clean
markContentClean();

// For user scripts, notify the editor is ready
editorReadyListeners().forEach(listener => listener(editor));
}

/**
Expand Down Expand Up @@ -181,6 +190,26 @@ export function handleMouseExited(_clientX: number, _clientY: number) {
setGutterHovered(false);
}

function observeContentHeightChanges(scrollDOM: HTMLElement) {
const notifyIfChanged = () => {
const panel = window.editor.dom.querySelector('.cm-panels-bottom');
const height = panel === null ? 0 : panel.getBoundingClientRect().height;
if (Math.abs(storage.bottomPanelHeight - height) < 0.001) {
return;
}

storage.bottomPanelHeight = height;
window.nativeModules.core.notifyContentHeightDidChange({
bottomPanelHeight: height,
});
};

// eslint-disable-next-line compat/compat
const observer = new ResizeObserver(notifyIfChanged);
observer.observe(scrollDOM);
notifyIfChanged();
}

function fixWebKitWheelIssues(scrollDOM: HTMLElement) {
// Fix the vertical scrollbar initially visible for short documents
scrollDOM.style.overflow = 'hidden';
Expand Down Expand Up @@ -220,7 +249,9 @@ function fixWebKitWheelIssues(scrollDOM: HTMLElement) {
const storage: {
scrollTimer: ReturnType<typeof setTimeout> | undefined;
viewportScale: number;
bottomPanelHeight: number;
} = {
scrollTimer: undefined,
viewportScale: 1.0,
bottomPanelHeight: 0.0,
};
Loading

0 comments on commit fb4ef23

Please sign in to comment.