Skip to content

Commit

Permalink
✨ feat: Optimize experience in normal mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Snowflyt committed Dec 19, 2023
1 parent 104563d commit 4213b5e
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 41 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typora-copilot",
"version": "0.2.3",
"version": "0.2.4",
"private": true,
"description": "GitHub Copilot plugin for Typora",
"keywords": [
Expand Down Expand Up @@ -69,6 +69,7 @@
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-sort-destructure-keys": "^1.5.0",
"happy-dom": "^12.10.3",
"hotscript": "^1.0.13",
"prettier": "^2.8.8",
"prettier-plugin-packagejson": "^2.4.7",
"rimraf": "^5.0.5",
Expand Down
6 changes: 3 additions & 3 deletions src/components/CompletionPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useEffect, useRef } from "preact/hooks";

import { COPILOT_ICON_PATHNAME } from "@/constants";
import { File } from "@/typora-utils";
import { getTextCursorPosition } from "@/utils/dom";
import { getCaretCoordinate } from "@/utils/dom";
import { css, registerCSS } from "@/utils/tools";

registerCSS(css`
Expand All @@ -31,7 +31,7 @@ export interface CompletionPanelProps {
}

export const attachCompletionPanel = (text: string) => {
const pos = getTextCursorPosition();
const pos = getCaretCoordinate();
if (!pos) return () => {};

const container = document.createElement("div");
Expand All @@ -41,7 +41,7 @@ export const attachCompletionPanel = (text: string) => {
render(<CompletionPanel x={x} y={y} text={text} />, container);

const scrollListener = () => {
const pos = getTextCursorPosition();
const pos = getCaretCoordinate();
if (!pos) return;
$(".completion-panel").css("top", `calc(${pos.y}px + 1.5em)`);
};
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { setGlobalVar } from "./utils/tools";
/**
* Plugin version.
*/
export const VERSION = "0.2.2";
export const VERSION = "0.2.4";

/**
* Copilot plugin directory.
Expand Down
12 changes: 6 additions & 6 deletions src/extracted.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ const Node = /** @type {typeof Typora.Node} */ (/** @type {unknown} */ (window.N
* Extracted functions (Extracted from Typora's bundled code, I don't understand them all) *
*******************************************************************************************/
/**
* Get the cursor placement of the current node.
* @returns {Typora.CursorPlacement | null}
* Get the caret placement of the current node.
* @returns {Typora.CaretPlacement | null}
*/
export const getCursorPlacement = () =>
export const getCaretPlacement = () =>
_getCursorPlacement.call(
/** @type {ExtendedFileConstructor & { get(key: string): string | undefined }} */ (File),
) ?? null;
/**
* @this {ExtendedFileConstructor & { get(key: string): string | undefined }}
* @param {Typora.CursorPlacement} [placement]
* @returns {Typora.CursorPlacement | undefined}
* @param {Typora.CaretPlacement} [placement]
* @returns {Typora.CaretPlacement | undefined}
*/
function _getCursorPlacement(placement) {
if (this.editor.focusCid) {
Expand Down Expand Up @@ -65,7 +65,7 @@ function _getCursorPlacement(placement) {
(s = !/`|~/.exec(activeNode.get("pattern") || "```")) || i++,
((placement = document.activeElement.classList.contains("ty-cm-lang-input")
? { line: -1, before: activeNode.get("pattern") || "```" }
: /** @type {Typora.CursorPlacement} */ (
: /** @type {Typora.CaretPlacement} */ (
// THIS IS TO FIX A BUG IN TYPORA, THE IMPLEMENTATION IS NOT THE SAME AS THE ORIGINAL
// Typora originally just mutate the cursor in place and return itself, which can cause bugs.
// Here I use a immutable way by creating a new one, which is slower but safer.
Expand Down
53 changes: 26 additions & 27 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import { logger } from "./logging";
import {
File,
TYPORA_VERSION,
getCursorPlacement,
getCaretPlacement,
waitUntilEditorInitialized,
} from "./typora-utils";
import { getTextCursorPosition } from "./utils/dom";
import { $S, getCaretCoordinate } from "./utils/dom";
import { css, registerCSS, setGlobalVar, sliceTextByRange } from "./utils/tools";

import type { Completion } from "./client";
Expand Down Expand Up @@ -70,10 +70,10 @@ const main = async () => {
(File.filePath ?? (File.bundle && File.bundle.filePath)) || null;

/**
* Get current cursor position in source markdown text.
* Get current caret position in source markdown text.
* @returns
*/
const getCursorPos = (): Position | null => {
const getCaretPosition = (): Position | null => {
// When selection, return null
if (sourceView.inSourceMode) {
if (cm.getSelection()) return null;
Expand All @@ -94,9 +94,9 @@ const main = async () => {
// @ts-expect-error - CodeMirror supports 2nd parameter, but not declared in types
cm.setValue(editor.getMarkdown(), "begin");

let placement: Typora.CursorPlacement | null;
let placement: Typora.CaretPlacement | null;
try {
placement = getCursorPlacement();
placement = getCaretPlacement();
} catch (e) {
if (e instanceof Error && e.stack) console.warn(e.stack);
return null;
Expand Down Expand Up @@ -154,14 +154,15 @@ const main = async () => {
// When not in writer, do not insert completion text
if ("BODY" === activeElement.tagName) return;

// If in a code block, use the cm way to insert completion text
// If in a CodeMirror instance, try to use the cm way to insert completion text
if ("TEXTAREA" === activeElement.tagName) {
const cms = $(activeElement).closest(".CodeMirror");
if (!cms || !cms.length) return;
const cm = (cms[0] as unknown as { CodeMirror: CodeMirror.Editor }).CodeMirror;
const subCmCompletion = { ...options };
const startPos = getCursorPos()!;
startPos.line -= cm.getValue().split(File.useCRLF ? "\r\n" : "\n").length - 1;
const startPos = getCaretPosition()!;
startPos.line -=
cm.getValue(File.useCRLF ? "\r\n" : "\n").split(File.useCRLF ? "\r\n" : "\n").length - 1;
startPos.character -= cm.getCursor().ch;
// Set `position` and `range` to be relative to `startPos`
subCmCompletion.position = {
Expand Down Expand Up @@ -208,7 +209,7 @@ const main = async () => {
if (!focusedElem) return;
if (!(focusedElem instanceof HTMLElement)) return;

const pos = getTextCursorPosition();
const pos = getCaretCoordinate();
if (!pos) return;

// Insert a completion panel below the cursor
Expand Down Expand Up @@ -247,7 +248,7 @@ const main = async () => {
// Calculate whether it is safe to just use `insertText` to insert completion text
let safeToJustUseInsertText = false;
let textToInsert = text;
const cursorPos = getCursorPos();
const cursorPos = getCaretPosition();
if (
cursorPos &&
cursorPos.line === range.end.line &&
Expand Down Expand Up @@ -305,7 +306,6 @@ const main = async () => {
const clearListeners = () => {
cleared = true;
editor.writingArea.removeEventListener("keydown", keydownHandler);
clearInterval(cursorMoveListener);
};

/**
Expand All @@ -327,14 +327,13 @@ const main = async () => {
};
editor.writingArea.addEventListener("keydown", keydownHandler, true);

const lastCursor = editor.lastCursor;
const cursorMoveListener = setInterval(() => {
$S(editor.writingArea).once("caretMove", () => {
if (cleared) return;

if (editor.lastCursor !== lastCursor) {
clearListeners();
_reject();
}
state.latestCaretMoveTimestamp = Date.now();

clearListeners();
_reject();
});

const reject = () => {
Expand Down Expand Up @@ -542,7 +541,7 @@ const main = async () => {
if (origin === "undo" || origin === "redo") {
if (sourceView.inSourceMode) cm[origin]();
else editor.undo[origin]();
state.latestCursorChangeTimestamp = Date.now();
state.latestCaretMoveTimestamp = Date.now();
} else {
cm.replaceRange(text.join(File.useCRLF ? "\r\n" : "\n"), from, to, origin);
}
Expand All @@ -556,7 +555,7 @@ const main = async () => {
const cursorMoveHandler = () => {
if (cleared) return;

state.latestCursorChangeTimestamp = Date.now();
state.latestCaretMoveTimestamp = Date.now();

clearListeners();
_reject();
Expand Down Expand Up @@ -637,12 +636,12 @@ const main = async () => {
});

/* Fetch completion from Copilot if cursor position exists */
const cursorPos = getCursorPos();
const cursorPos = getCaretPosition();
if (!cursorPos) return;

// Fetch completion from Copilot
const changeTimestamp = state.latestChangeTimestamp;
const cursorChangeTimestamp = state.latestCursorChangeTimestamp;
const caretMoveTimestamp = state.latestCaretMoveTimestamp;
const { cancellationReason, completions } = await copilot.request.getCompletions({
position: cursorPos,
path: state.activeFilePathname,
Expand All @@ -653,7 +652,7 @@ const main = async () => {

if (
state.latestChangeTimestamp !== changeTimestamp ||
state.latestCursorChangeTimestamp !== cursorChangeTimestamp
state.latestCaretMoveTimestamp !== caretMoveTimestamp
) {
if (state.latestChangeTimestamp !== changeTimestamp)
logger.debug(
Expand All @@ -664,8 +663,8 @@ const main = async () => {
else
logger.debug(
"Ignoring completion due to text cursor change timestamp mismatch",
state.latestCursorChangeTimestamp,
cursorChangeTimestamp,
state.latestCaretMoveTimestamp,
caretMoveTimestamp,
);
completions.forEach((completion) => {
copilot.notification.notifyRejected({ uuids: [completion.uuid] });
Expand Down Expand Up @@ -709,7 +708,7 @@ const main = async () => {
markdown: editor.getMarkdown(),
_actualLatestMarkdown: editor.getMarkdown(),
latestChangeTimestamp: Date.now(),
latestCursorChangeTimestamp: Date.now(),
latestCaretMoveTimestamp: Date.now(),
suppressMarkdownChange: 0,
};
// Initialize CodeMirror
Expand Down Expand Up @@ -818,7 +817,7 @@ const main = async () => {
cm.on("change", (cm, change) => {
if (!editor.sourceView.inSourceMode) return;

const newMarkdown = cm.getValue();
const newMarkdown = cm.getValue(File.useCRLF ? "\r\n" : "\n");
// If not literally changed, simply return
if (newMarkdown === state._actualLatestMarkdown) return;
// If literally changed, update current actual markdown text
Expand Down
2 changes: 1 addition & 1 deletion src/typora-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ declare namespace Typora {
* Representing the text cursor position in raw markdown. Can be considered as an enhanced version
* of {@link CodeMirrorDocumentPosition}.
*/
interface CursorPlacement {
interface CaretPlacement {
line: number;
ch?: number;
before?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/typora-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,4 @@ export const waitUntilEditorInitialized = (): Promise<void> =>
/*******************************************************************************************
* Extracted functions (Extracted from Typora's bundled code, I don't understand them all) *
*******************************************************************************************/
export { getCursorPlacement } from "./extracted";
export { getCaretPlacement } from "./extracted";
Loading

0 comments on commit 4213b5e

Please sign in to comment.