Skip to content

Commit 53fa39d

Browse files
author
Jannes
committed
upgrade codemirror api
1 parent a48ce2c commit 53fa39d

File tree

7 files changed

+151
-83
lines changed

7 files changed

+151
-83
lines changed

.github/workflows/npm-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- uses: actions/checkout@v3
1616
- uses: actions/setup-node@v3
1717
with:
18-
node-version: 16
18+
node-version: 18
1919
- run: npm ci
2020
- run: npm run dist
2121

package-lock.json

Lines changed: 90 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "joplin-plugin-zotero-link",
3-
"version": "1.2.1",
3+
"version": "2.0.0",
44
"scripts": {
55
"dist": "webpack --env joplin-plugin-config=buildMain && webpack --env joplin-plugin-config=buildExtraScripts && webpack --env joplin-plugin-config=createArchive",
66
"prepare": "npm run dist",
@@ -28,6 +28,9 @@
2828
"webpack-cli": "^4.10.0"
2929
},
3030
"dependencies": {
31+
"@codemirror/autocomplete": "^6.18.1",
32+
"@codemirror/language": "^6.10.3",
33+
"@codemirror/view": "^6.34.1",
3134
"@types/codemirror": "^5.60.15"
3235
}
3336
}

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ joplin.plugins.register({
2222
await joplin.contentScripts.onMessage(scriptId, async (msg) => {
2323

2424
if (msg === 'getSettings') {
25-
console.log('received getSettings');
2625
const settingValue = await joplin.settings.value(SETTING.Port);
2726
return {
2827
port: settingValue,

src/zotero-item.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { Completion, pickedCompletion } from "@codemirror/autocomplete";
2+
import { EditorView } from "@codemirror/view";
3+
import { Annotation, AnnotationType, ChangeSet, Text } from "@codemirror/state";
14
import { Editor } from "codemirror";
25

36
export class ZoteroItem {
@@ -37,24 +40,27 @@ export class ZoteroItem {
3740
return value ? value.indexOf(query) >= 0 : false;
3841
}
3942

40-
getHint() {
43+
getHint(): Completion {
4144
return {
42-
text: this.title,
43-
render: async (elem, self, data) => {
44-
const itemElem = elem.ownerDocument.createElement('div');
45-
itemElem.className = 'zotero-results-item';
46-
47-
itemElem.innerHTML = `
48-
<div class="zotero-item-title">${this.title}</div>
49-
<div class="zotero-item-meta">${this.creators} <i>${this.date}</i></div>
50-
`;
51-
elem.appendChild(itemElem);
52-
},
53-
hint: async (cm: Editor, data, completion) => {
54-
const from = completion.from || data.from;
55-
from.ch -= 2;
56-
cm.replaceRange(`[${this.title}](zotero://select/library/items/${this.key})`, from, cm.getCursor(), "complete");
57-
}
45+
label: 'z@' + this.titleL + this.creatorsL + this.date,
46+
displayLabel: this.title,
47+
detail: this.creators + ' ' + this.date,
48+
apply: this.apply.bind(this)
49+
};
5850
}
51+
52+
apply(view: EditorView, completion: Completion, from: number, to: number) {
53+
const replacement = `[${this.title}](zotero://select/library/items/${this.key})`;
54+
55+
const replaceTransaction = view.state.update({
56+
changes: {
57+
from: from-2,
58+
to,
59+
insert: replacement
60+
},
61+
annotations: pickedCompletion.of(completion)
62+
});
63+
64+
view.dispatch(replaceTransaction);
5965
}
6066
}

src/zotero-link.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
11
import { ZoteroQuery } from './zotero-query';
2+
import { completeFromList, CompletionContext } from '@codemirror/autocomplete';
23

34
module.exports = {
45
default: function(context) {
56

67
const plugin = function (CodeMirror) {
7-
88
CodeMirror.defineOption('zoteroLink', false, function(cm, value, prev) {
99
if (!value) return;
10-
const zq = new ZoteroQuery(CodeMirror, context, cm);
10+
const zq = new ZoteroQuery(context, cm);
1111

12-
cm.on('inputRead', zq.getInputReadHandler());
12+
console.log(CodeMirror);
13+
CodeMirror.addExtension([
14+
CodeMirror.joplinExtensions.completionSource(
15+
zq.getCompletions()
16+
),
17+
18+
// Joplin also exposes a Facet that allows enabling or disabling CodeMirror's
19+
// built-in autocompletions. These apply, for example, to HTML tags.
20+
CodeMirror.joplinExtensions.enableLanguageDataAutocomplete.of(true)
21+
]);
1322
});
1423
};
1524

1625
return {
1726
plugin,
18-
codeMirrorResources: [
19-
'addon/hint/show-hint'
20-
],
2127
codeMirrorOptions: {
2228
'zoteroLink': true
2329
},

src/zotero-query.ts

Lines changed: 21 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { start } from "repl";
12
import { SETTING } from "./settings";
23
import { ZoteroItem } from "./zotero-item";
34

5+
import { CompletionContext, startCompletion } from "@codemirror/autocomplete";
6+
47
interface ChangePos {
58
line: any;
69
ch: any;
@@ -14,69 +17,34 @@ interface Change {
1417
const COMMAND = 'z@';
1518

1619
export class ZoteroQuery {
17-
codeMirror: any;
18-
cm: any;
19-
context: any;
2020
start: ChangePos;
2121
change: Change;
2222
zoteroItems: ZoteroItem[] = [];
2323
filteredItems: ZoteroItem[] = [];
2424
timeout: NodeJS.Timeout | undefined;
2525

26-
constructor(codeMirror, context, cm) {
27-
this.codeMirror = codeMirror;
28-
this.context = context;
29-
this.cm = cm
30-
}
31-
32-
getInputReadHandler() {
33-
const that = this;
34-
return async function (cm, change) {
35-
if (!cm.state.completionActive && that.cm.getTokenAt(cm.getCursor()).string === COMMAND) {
36-
that.zoteroItems = await that.loadData();
37-
that.filteredItems = that.zoteroItems;
38-
39-
that.change = change;
40-
that.start = {
41-
line: change.from.line,
42-
ch: change.from.ch + 1
43-
};
44-
45-
setTimeout(() => {
46-
that.codeMirror.showHint(that.cm, that.getPrepareHints(), {
47-
completeSingle: false,
48-
closeOnUnfocus: true,
49-
async: true,
50-
closeCharacters: /[()\[\]{};:>,]/
51-
});
52-
}, 10);
53-
}
54-
}
26+
constructor(private context, private cm) {
27+
this.loadData().then(items => this.zoteroItems = items);
5528
}
5629

57-
getPrepareHints() {
30+
getCompletions() {
5831
const that = this;
59-
60-
return async function (cm, callback) {
61-
const cursor = cm.getCursor();
62-
let prefix = cm.getRange(that.start, cursor) || '';
63-
64-
const hints = await that.query(prefix);
65-
callback({
66-
list: hints,
67-
from: {
68-
line: that.change.from.line,
69-
ch: that.change.from.ch + 1,
70-
},
71-
to: {
72-
line: that.change.to.line,
73-
ch: that.change.to.ch + 1,
74-
},
75-
});
32+
return (context: CompletionContext) => {
33+
// reload data in background
34+
that.loadData().then(items => that.zoteroItems = items);
35+
let word = context.matchBefore(/\S+/);
36+
37+
if (!word || word.from == word.to || !word.text.startsWith('z@'))
38+
return null;
39+
40+
return {
41+
from: word.from + 2,
42+
options: that.zoteroItems.map(item => item.getHint())
43+
}
7644
}
77-
};
45+
}
7846

79-
async query(query: string) {
47+
query(query: string) {
8048
if (!this.zoteroItems) {
8149
return [];
8250
}
@@ -99,12 +67,10 @@ export class ZoteroQuery {
9967
data = await this.tryZotServer(settings.port);
10068
}
10169

102-
console.log(data);
103-
10470
if (!!data) {
10571
data = data.filter(item => item.itemType !== 'attachment' && item.itemType !== 'note')
10672
.map(item => new ZoteroItem(item));
107-
console.log('items', data);
73+
10874
data.sort((a: ZoteroItem, b: ZoteroItem) => a.title.localeCompare(b.title));
10975
return data;
11076
} else {

0 commit comments

Comments
 (0)