Skip to content

Commit e943ddc

Browse files
committed
Autocomplete issues or pull-request ids
1 parent 2abdbe8 commit e943ddc

File tree

3 files changed

+106
-8
lines changed

3 files changed

+106
-8
lines changed

templates/shared/combomarkdowneditor.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Template Attributes:
4444
<button class="markdown-toolbar-button markdown-switch-easymde" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.switch_to_legacy.tooltip"}}">{{svg "octicon-arrow-switch"}}</button>
4545
</div>
4646
</markdown-toolbar>
47-
<text-expander keys=": @" suffix="">
47+
<text-expander keys=": @ #" multiword="#" suffix="">
4848
<textarea class="markdown-text-editor"{{if .TextareaName}} name="{{.TextareaName}}"{{end}}{{if .TextareaPlaceholder}} placeholder="{{.TextareaPlaceholder}}"{{end}}{{if .TextareaAriaLabel}} aria-label="{{.TextareaAriaLabel}}"{{end}}{{if .DisableAutosize}} data-disable-autosize="{{.DisableAutosize}}"{{end}}>{{.TextareaContent}}</textarea>
4949
</text-expander>
5050
<script>

web_src/js/features/comp/TextExpander.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import {matchEmoji, matchMention} from '../../utils/match.ts';
1+
import {matchEmoji, matchMention, matchIssueOrPullRequest} from '../../utils/match.ts';
22
import {emojiString} from '../emoji.ts';
33

44
export function initTextExpander(expander) {
5-
expander?.addEventListener('text-expander-change', ({detail: {key, provide, text}}) => {
5+
expander?.addEventListener('text-expander-change', async ({detail: {key, provide, text}}) => {
66
if (key === ':') {
77
const matches = matchEmoji(text);
88
if (!matches.length) return provide({matched: false});
@@ -48,13 +48,37 @@ export function initTextExpander(expander) {
4848
ul.append(li);
4949
}
5050

51+
provide({matched: true, fragment: ul});
52+
} else if (key === '#') {
53+
const url = window.location.href;
54+
const matches = matchIssueOrPullRequest(url, text);
55+
if (!matches.length) return provide({matched: false});
56+
57+
const ul = document.createElement('ul');
58+
ul.classList.add('suggestions');
59+
for (const {value, name, type} of matches) {
60+
const li = document.createElement('li');
61+
li.setAttribute('role', 'option');
62+
li.setAttribute('data-value', `${key}${value}`);
63+
64+
const icon = document.createElement('span');
65+
icon.classList.add('icon', type === 'issue' ? 'issue' : 'pull-request');
66+
li.append(icon);
67+
68+
const nameSpan = document.createElement('span');
69+
nameSpan.textContent = name;
70+
li.append(nameSpan);
71+
72+
ul.append(li);
73+
}
74+
5175
provide({matched: true, fragment: ul});
5276
}
5377
});
5478
expander?.addEventListener('text-expander-value', ({detail}) => {
5579
if (detail?.item) {
56-
// add a space after @mentions as it's likely the user wants one
57-
const suffix = detail.key === '@' ? ' ' : '';
80+
// add a space after @mentions and #issue as it's likely the user wants one
81+
const suffix = ['@', '#'].includes(detail.key) ? ' ' : '';
5882
detail.value = `${detail.item.getAttribute('data-value')}${suffix}`;
5983
}
6084
});

web_src/js/utils/match.ts

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import emojis from '../../../assets/emoji.json';
2+
import { request } from '../modules/fetch.ts';
23

34
const maxMatches = 6;
45

5-
function sortAndReduce(map: Map<string, number>) {
6+
function sortAndReduce<T>(map: Map<T, number>): T[] {
67
const sortedMap = new Map(Array.from(map.entries()).sort((a, b) => a[1] - b[1]));
78
return Array.from(sortedMap.keys()).slice(0, maxMatches);
89
}
@@ -27,11 +28,12 @@ export function matchEmoji(queryText: string): string[] {
2728
return sortAndReduce(results);
2829
}
2930

30-
export function matchMention(queryText: string): string[] {
31+
type Mention = {value: string; name: string; fullname: string; avatar: string};
32+
export function matchMention(queryText: string): Mention[] {
3133
const query = queryText.toLowerCase();
3234

3335
// results is a map of weights, lower is better
34-
const results = new Map();
36+
const results = new Map<Mention, number>();
3537
for (const obj of window.config.mentionValues ?? []) {
3638
const index = obj.key.toLowerCase().indexOf(query);
3739
if (index === -1) continue;
@@ -41,3 +43,75 @@ export function matchMention(queryText: string): string[] {
4143

4244
return sortAndReduce(results);
4345
}
46+
47+
type IssueOrPullRequest = {value: string; name: string; type: 'issue' | 'pull-request'};
48+
export async function matchIssueOrPullRequest(url: string, queryText: string): IssueOrPullRequest[] {
49+
const query = queryText.toLowerCase();
50+
51+
const repository = url.split('/').slice(-2).join('/');
52+
const issuePullRequestId = url.split('/').slice(-1)[0];
53+
54+
console.log('suggestions for', {
55+
repository,
56+
query,
57+
});
58+
59+
// TODO: fetch data from api
60+
// const res = await request('/-/suggestions', {
61+
// method: 'GET',
62+
// data: {
63+
// repository,
64+
// query,
65+
// },
66+
// });
67+
// console.log(await res.json());
68+
69+
// results is a map of weights, lower is better
70+
const results = new Map<IssueOrPullRequest, number>();
71+
// for (const obj of window.config.mentionValues ?? []) {
72+
// const index = obj.key.toLowerCase().indexOf(query);
73+
// if (index === -1) continue;
74+
// const existing = results.get(obj);
75+
// results.set(obj, existing ? existing - index : index);
76+
// }
77+
78+
results.set({
79+
value: '28958',
80+
name: 'Live removal of issue comments using htmx websocket',
81+
type: 'pull-request',
82+
}, 0);
83+
84+
results.set({
85+
value: '32234',
86+
name: 'Calculate `PublicOnly` for org membership only once',
87+
type: 'pull-request',
88+
}, 1);
89+
90+
results.set({
91+
value: '32280',
92+
name: 'Optimize branch protection rule loading',
93+
type: 'pull-request',
94+
}, 2);
95+
96+
results.set({
97+
value: '32326',
98+
name: 'Shallow Mirroring',
99+
type: 'issue',
100+
}, 3);
101+
102+
results.set({
103+
value: '32248',
104+
name: 'Make admins adhere to branch protection rules',
105+
type: 'pull-request',
106+
}, 4);
107+
108+
// filter out current issue/pull request
109+
for (const [key] of results.entries()) {
110+
if (key.value === issuePullRequestId) {
111+
results.delete(key);
112+
break;
113+
}
114+
}
115+
116+
return sortAndReduce(results);
117+
}

0 commit comments

Comments
 (0)