@@ -4,144 +4,37 @@ import { GlyphChars } from '../constants';
44import type { IntegrationId } from '../constants.integrations' ;
55import { IssueIntegrationId } from '../constants.integrations' ;
66import type { Container } from '../container' ;
7- import type { IssueOrPullRequest } from '../git/models/issue' ;
87import type { GitRemote } from '../git/models/remote' ;
9- import type { ProviderReference } from '../git/models/remoteProvider' ;
108import { 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' ;
1210import { fromNow } from '../system/date' ;
1311import { debug } from '../system/decorators/log' ;
1412import { encodeUrl } from '../system/encoding' ;
1513import { join , map } from '../system/iterable' ;
1614import { Logger } from '../system/logger' ;
1715import { escapeMarkdown } from '../system/markdown' ;
18- import type { MaybePausedResult } from '../system/promise' ;
1916import { getSettledValue , isPromise } from '../system/promise' ;
20- import { capitalize , encodeHtmlWeak , escapeRegex , getSuperscript } from '../system/string' ;
17+ import { capitalize , encodeHtmlWeak , getSuperscript } from '../system/string' ;
2118import { 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' ;
2235
2336const emptyAutolinkMap = Object . freeze ( new Map < string , Autolink > ( ) ) ;
2437
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-
14538export class Autolinks implements Disposable {
14639 protected _disposable : Disposable | undefined ;
14740 private _references : CacheableAutolinkReference [ ] = [ ] ;
@@ -629,151 +522,3 @@ export class Autolinks implements Disposable {
629522 return true ;
630523 }
631524}
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