@@ -4,144 +4,37 @@ import { GlyphChars } from '../constants';
4
4
import type { IntegrationId } from '../constants.integrations' ;
5
5
import { IssueIntegrationId } from '../constants.integrations' ;
6
6
import type { Container } from '../container' ;
7
- import type { IssueOrPullRequest } from '../git/models/issue' ;
8
7
import type { GitRemote } from '../git/models/remote' ;
9
- import type { ProviderReference } from '../git/models/remoteProvider' ;
10
8
import { getIssueOrPullRequestHtmlIcon , getIssueOrPullRequestMarkdownIcon } from '../git/utils/icons' ;
11
- import type { HostingIntegration , IssueIntegration , ResourceDescriptor } from '../plus/integrations/integration' ;
9
+ import type { HostingIntegration , IssueIntegration } from '../plus/integrations/integration' ;
12
10
import { fromNow } from '../system/date' ;
13
11
import { debug } from '../system/decorators/log' ;
14
12
import { encodeUrl } from '../system/encoding' ;
15
13
import { join , map } from '../system/iterable' ;
16
14
import { Logger } from '../system/logger' ;
17
15
import { escapeMarkdown } from '../system/markdown' ;
18
- import type { MaybePausedResult } from '../system/promise' ;
19
16
import { getSettledValue , isPromise } from '../system/promise' ;
20
- import { capitalize , encodeHtmlWeak , escapeRegex , getSuperscript } from '../system/string' ;
17
+ import { capitalize , encodeHtmlWeak , getSuperscript } from '../system/string' ;
21
18
import { configuration } from '../system/vscode/configuration' ;
19
+ import type {
20
+ Autolink ,
21
+ CacheableAutolinkReference ,
22
+ DynamicAutolinkReference ,
23
+ EnrichedAutolink ,
24
+ MaybeEnrichedAutolink ,
25
+ RefSet ,
26
+ } from './autolinks.utils' ;
27
+ import {
28
+ ensureCachedRegex ,
29
+ getAutolinks ,
30
+ getBranchAutolinks ,
31
+ isDynamic ,
32
+ numRegex ,
33
+ supportedAutolinkIntegrations ,
34
+ } from './autolinks.utils' ;
22
35
23
36
const emptyAutolinkMap = Object . freeze ( new Map < string , Autolink > ( ) ) ;
24
37
25
- const numRegex = / < n u m > / g;
26
-
27
- export type AutolinkType = 'issue' | 'pullrequest' ;
28
- export type AutolinkReferenceType = 'commit' | 'branch' ;
29
-
30
- export interface AutolinkReference {
31
- /** Short prefix to match to generate autolinks for the external resource */
32
- readonly prefix : string ;
33
- /** URL of the external resource to link to */
34
- readonly url : string ;
35
- /** Whether alphanumeric characters should be allowed in `<num>` */
36
- readonly alphanumeric : boolean ;
37
- /** Whether case should be ignored when matching the prefix */
38
- readonly ignoreCase : boolean ;
39
- readonly title : string | undefined ;
40
-
41
- readonly type ?: AutolinkType ;
42
- readonly referenceType ?: AutolinkReferenceType ;
43
- readonly description ?: string ;
44
- readonly descriptor ?: ResourceDescriptor ;
45
- }
46
-
47
- export interface Autolink extends AutolinkReference {
48
- provider ?: ProviderReference ;
49
- id : string ;
50
- index ?: number ;
51
-
52
- tokenize ?:
53
- | ( (
54
- text : string ,
55
- outputFormat : 'html' | 'markdown' | 'plaintext' ,
56
- tokenMapping : Map < string , string > ,
57
- enrichedAutolinks ?: Map < string , MaybeEnrichedAutolink > ,
58
- prs ?: Set < string > ,
59
- footnotes ?: Map < number , string > ,
60
- ) => string )
61
- | null ;
62
- }
63
-
64
- export type EnrichedAutolink = [
65
- issueOrPullRequest : Promise < IssueOrPullRequest | undefined > | undefined ,
66
- autolink : Autolink ,
67
- ] ;
68
-
69
- export type MaybeEnrichedAutolink = readonly [
70
- issueOrPullRequest : MaybePausedResult < IssueOrPullRequest | undefined > | undefined ,
71
- autolink : Autolink ,
72
- ] ;
73
-
74
- export function serializeAutolink ( value : Autolink ) : Autolink {
75
- const serialized : Autolink = {
76
- provider : value . provider
77
- ? {
78
- id : value . provider . id ,
79
- name : value . provider . name ,
80
- domain : value . provider . domain ,
81
- icon : value . provider . icon ,
82
- }
83
- : undefined ,
84
- id : value . id ,
85
- index : value . index ,
86
- prefix : value . prefix ,
87
- url : value . url ,
88
- alphanumeric : value . alphanumeric ,
89
- ignoreCase : value . ignoreCase ,
90
- title : value . title ,
91
- type : value . type ,
92
- description : value . description ,
93
- descriptor : value . descriptor ,
94
- } ;
95
- return serialized ;
96
- }
97
-
98
- export interface CacheableAutolinkReference extends AutolinkReference {
99
- tokenize ?:
100
- | ( (
101
- text : string ,
102
- outputFormat : 'html' | 'markdown' | 'plaintext' ,
103
- tokenMapping : Map < string , string > ,
104
- enrichedAutolinks ?: Map < string , MaybeEnrichedAutolink > ,
105
- prs ?: Set < string > ,
106
- footnotes ?: Map < number , string > ,
107
- ) => string )
108
- | null ;
109
-
110
- messageHtmlRegex ?: RegExp ;
111
- messageMarkdownRegex ?: RegExp ;
112
- messageRegex ?: RegExp ;
113
- branchNameRegex ?: RegExp ;
114
- }
115
-
116
- export interface DynamicAutolinkReference {
117
- tokenize ?:
118
- | ( (
119
- text : string ,
120
- outputFormat : 'html' | 'markdown' | 'plaintext' ,
121
- tokenMapping : Map < string , string > ,
122
- enrichedAutolinks ?: Map < string , MaybeEnrichedAutolink > ,
123
- prs ?: Set < string > ,
124
- footnotes ?: Map < number , string > ,
125
- ) => string )
126
- | null ;
127
- parse : ( text : string , autolinks : Map < string , Autolink > ) => void ;
128
- }
129
-
130
- export const supportedAutolinkIntegrations = [ IssueIntegrationId . Jira ] ;
131
-
132
- function isDynamic ( ref : AutolinkReference | DynamicAutolinkReference ) : ref is DynamicAutolinkReference {
133
- return ! ( 'prefix' in ref ) && ! ( 'url' in ref ) ;
134
- }
135
-
136
- function isCacheable ( ref : AutolinkReference | DynamicAutolinkReference ) : ref is CacheableAutolinkReference {
137
- return 'prefix' in ref && ref . prefix != null && 'url' in ref && ref . url != null ;
138
- }
139
-
140
- export type RefSet = [
141
- ProviderReference | undefined ,
142
- ( AutolinkReference | DynamicAutolinkReference ) [ ] | CacheableAutolinkReference [ ] ,
143
- ] ;
144
-
145
38
export class Autolinks implements Disposable {
146
39
protected _disposable : Disposable | undefined ;
147
40
private _references : CacheableAutolinkReference [ ] = [ ] ;
@@ -629,151 +522,3 @@ export class Autolinks implements Disposable {
629
522
return true ;
630
523
}
631
524
}
632
-
633
- function ensureCachedRegex (
634
- ref : CacheableAutolinkReference ,
635
- outputFormat : 'html' ,
636
- ) : asserts ref is RequireSome < CacheableAutolinkReference , 'messageHtmlRegex' > ;
637
- function ensureCachedRegex (
638
- ref : CacheableAutolinkReference ,
639
- outputFormat : 'markdown' ,
640
- ) : asserts ref is RequireSome < CacheableAutolinkReference , 'messageMarkdownRegex' > ;
641
- function ensureCachedRegex (
642
- ref : CacheableAutolinkReference ,
643
- outputFormat : 'plaintext' ,
644
- ) : asserts ref is RequireSome < CacheableAutolinkReference , 'messageRegex' | 'branchNameRegex' > ;
645
- function ensureCachedRegex ( ref : CacheableAutolinkReference , outputFormat : 'html' | 'markdown' | 'plaintext' ) {
646
- // Regexes matches the ref prefix followed by a token (e.g. #1234)
647
- if ( outputFormat === 'markdown' && ref . messageMarkdownRegex == null ) {
648
- // Extra `\\\\` in `\\\\\\[` is because the markdown is escaped
649
- ref . messageMarkdownRegex = new RegExp (
650
- `(^|\\s|\\(|\\[|\\{)(${ escapeRegex ( encodeHtmlWeak ( escapeMarkdown ( ref . prefix ) ) ) } (${
651
- ref . alphanumeric ? '\\w' : '\\d'
652
- } +))\\b`,
653
- ref . ignoreCase ? 'gi' : 'g' ,
654
- ) ;
655
- } else if ( outputFormat === 'html' && ref . messageHtmlRegex == null ) {
656
- ref . messageHtmlRegex = new RegExp (
657
- `(^|\\s|\\(|\\[|\\{)(${ escapeRegex ( encodeHtmlWeak ( ref . prefix ) ) } (${ ref . alphanumeric ? '\\w' : '\\d' } +))\\b` ,
658
- ref . ignoreCase ? 'gi' : 'g' ,
659
- ) ;
660
- } else if ( ref . messageRegex == null ) {
661
- ref . messageRegex = new RegExp (
662
- `(^|\\s|\\(|\\[|\\{)(${ escapeRegex ( ref . prefix ) } (${ ref . alphanumeric ? '\\w' : '\\d' } +))\\b` ,
663
- ref . ignoreCase ? 'gi' : 'g' ,
664
- ) ;
665
- ref . branchNameRegex = new RegExp (
666
- `(^|\\-|_|\\.|\\/)(?<prefix>${ ref . prefix } )(?<issueKeyNumber>${
667
- ref . alphanumeric ? '\\w' : '\\d'
668
- } +)(?=$|\\-|_|\\.|\\/)`,
669
- 'gi' ,
670
- ) ;
671
- }
672
-
673
- return true ;
674
- }
675
-
676
- /**
677
- * Compares autolinks
678
- * @returns non-0 result that means a probability of the autolink `b` is more relevant of the autolink `a`
679
- */
680
- function compareAutolinks ( a : Autolink , b : Autolink ) : number {
681
- // consider that if the number is in the start, it's the most relevant link
682
- if ( b . index === 0 ) return 1 ;
683
- if ( a . index === 0 ) return - 1 ;
684
-
685
- // maybe it worths to use some weight function instead.
686
- return (
687
- b . prefix . length - a . prefix . length ||
688
- b . id . length - a . id . length ||
689
- ( b . index != null && a . index != null ? - ( b . index - a . index ) : 0 )
690
- ) ;
691
- }
692
-
693
- export function getAutolinks ( message : string , refsets : Readonly < RefSet [ ] > ) {
694
- const autolinks = new Map < string , Autolink > ( ) ;
695
-
696
- let match ;
697
- let num ;
698
- for ( const [ provider , refs ] of refsets ) {
699
- for ( const ref of refs ) {
700
- if ( ! isCacheable ( ref ) || ( ref . referenceType && ref . referenceType !== 'commit' ) ) {
701
- if ( isDynamic ( ref ) ) {
702
- ref . parse ( message , autolinks ) ;
703
- }
704
- continue ;
705
- }
706
-
707
- ensureCachedRegex ( ref , 'plaintext' ) ;
708
-
709
- do {
710
- match = ref . messageRegex . exec ( message ) ;
711
- if ( ! match ) break ;
712
-
713
- [ , , , num ] = match ;
714
-
715
- autolinks . set ( num , {
716
- provider : provider ,
717
- id : num ,
718
- index : match . index ,
719
- prefix : ref . prefix ,
720
- url : ref . url ?. replace ( numRegex , num ) ,
721
- alphanumeric : ref . alphanumeric ,
722
- ignoreCase : ref . ignoreCase ,
723
- title : ref . title ?. replace ( numRegex , num ) ,
724
- type : ref . type ,
725
- description : ref . description ?. replace ( numRegex , num ) ,
726
- descriptor : ref . descriptor ,
727
- } ) ;
728
- } while ( true ) ;
729
- }
730
- }
731
-
732
- return autolinks ;
733
- }
734
-
735
- export function getBranchAutolinks ( branchName : string , refsets : Readonly < RefSet [ ] > ) {
736
- const autolinks = new Map < string , Autolink > ( ) ;
737
-
738
- let match ;
739
- let num ;
740
- for ( const [ provider , refs ] of refsets ) {
741
- for ( const ref of refs ) {
742
- if (
743
- ! isCacheable ( ref ) ||
744
- ref . type === 'pullrequest' ||
745
- ( ref . referenceType && ref . referenceType !== 'branch' )
746
- ) {
747
- continue ;
748
- }
749
-
750
- ensureCachedRegex ( ref , 'plaintext' ) ;
751
- const matches = branchName . matchAll ( ref . branchNameRegex ) ;
752
- do {
753
- match = matches . next ( ) ;
754
- if ( ! match . value ?. groups ) break ;
755
-
756
- num = match ?. value ?. groups . issueKeyNumber ;
757
- let index = match . value . index ;
758
- const linkUrl = ref . url ?. replace ( numRegex , num ) ;
759
- // strange case (I would say synthetic), but if we parse the link twice, use the most relevant of them
760
- const existingIndex = autolinks . get ( linkUrl ) ?. index ;
761
- if ( existingIndex != null ) {
762
- index = Math . min ( index , existingIndex ) ;
763
- }
764
- autolinks . set ( linkUrl , {
765
- ...ref ,
766
- provider : provider ,
767
- id : num ,
768
- index : index ,
769
- url : linkUrl ,
770
- title : ref . title ?. replace ( numRegex , num ) ,
771
- description : ref . description ?. replace ( numRegex , num ) ,
772
- descriptor : ref . descriptor ,
773
- } ) ;
774
- } while ( ! match . done ) ;
775
- }
776
- }
777
-
778
- return new Map ( [ ...autolinks . entries ( ) ] . sort ( ( a , b ) => compareAutolinks ( a [ 1 ] , b [ 1 ] ) ) ) ;
779
- }
0 commit comments