@@ -23,32 +23,12 @@ export function getLanguageModeRecommendationSearchKeyword(filename) {
2323 return keyword ;
2424}
2525
26- function getIssueUrl ( keyword ) {
27- const params = new URLSearchParams ( {
28- template : "1_feature_request.yml" ,
29- labels : "new plugin idea,enhancement" ,
30- title : `Plugin request: ${ keyword } syntax highlighting` ,
31- } ) ;
32-
33- return `${ config . GITHUB_URL } /issues/new?${ params } ` ;
34- }
35-
3626function formatString ( value , replacements ) {
3727 return String ( value || "" ) . replace ( / \{ ( \w + ) \} / g, ( _ , key ) => {
3828 return replacements [ key ] ?? "" ;
3929 } ) ;
4030}
4131
42- async function openUrl ( url ) {
43- if ( window . cordova ?. exec ) {
44- const { default : customTab } = await import ( "./customTab" ) ;
45- await customTab ( url ) ;
46- return ;
47- }
48-
49- window . open ( url , "_blank" , "noopener,noreferrer" ) ;
50- }
51-
5232async function openExtensions ( keyword ) {
5333 const { openWithSearch } = await import ( "sidebarApps/extensions" ) ;
5434 openWithSearch ( keyword ) ;
@@ -58,6 +38,19 @@ function hasPlainTextFallback(modeInfo, filename) {
5838 return modeInfo ?. name === "text" && ! modeInfo . supportsFile ( filename ) ;
5939}
6040
41+ export function shouldRecommendLanguageModeExtension ( filename , modeInfo ) {
42+ if ( ! hasPlainTextFallback ( modeInfo , filename ) ) return false ;
43+
44+ const keyword = getLanguageModeRecommendationSearchKeyword ( filename ) ;
45+ if ( ! keyword ) return false ;
46+
47+ // Probe the normalized extension independently of the original filename.
48+ // This prevents a strangely formatted path from producing requests for core
49+ // modes such as HTML or Python.
50+ const probeFilename = `file.${ keyword } ` ;
51+ return hasPlainTextFallback ( getModeForPath ( probeFilename ) , probeFilename ) ;
52+ }
53+
6154class LanguageModeRecommendations {
6255 notifiedKeywords = new Set ( ) ;
6356 pendingKeywords = new Set ( ) ;
@@ -76,9 +69,21 @@ class LanguageModeRecommendations {
7669 ) ,
7770 ) ,
7871 )
79- . then ( ( response ) => ( response . ok ? response . json ( ) : [ ] ) )
72+ . then ( ( response ) => {
73+ if ( ! response . ok ) {
74+ throw new Error ( `Plugin registry request failed: ${ response . status } ` ) ;
75+ }
76+ return response . json ( ) ;
77+ } )
8078 . then ( ( plugins ) => Array . isArray ( plugins ) && plugins . length > 0 )
81- . catch ( ( ) => false ) ;
79+ . catch ( ( ) => {
80+ // Do not let a temporary network or server failure suppress this
81+ // recommendation for the rest of the app session.
82+ if ( this . availabilityCache . get ( keyword ) === availability ) {
83+ this . availabilityCache . delete ( keyword ) ;
84+ }
85+ return false ;
86+ } ) ;
8287
8388 this . availabilityCache . set ( keyword , availability ) ;
8489 return availability ;
@@ -88,7 +93,7 @@ class LanguageModeRecommendations {
8893 if ( ! file || file . type !== "editor" ) return ;
8994
9095 const filename = file . filename || "" ;
91- if ( ! hasPlainTextFallback ( modeInfo , filename ) ) return ;
96+ if ( ! shouldRecommendLanguageModeExtension ( filename , modeInfo ) ) return ;
9297
9398 const keyword = getLanguageModeRecommendationSearchKeyword ( filename ) ;
9499 if (
@@ -116,52 +121,35 @@ class LanguageModeRecommendations {
116121 const hasPlugins = await this . getPluginAvailability ( keyword ) ;
117122 // If a plugin registered the mode while the lookup was pending, suppress
118123 // this stale recommendation and leave the keyword eligible for future checks.
119- if ( ! hasPlainTextFallback ( getModeForPath ( filename ) , filename ) ) return false ;
120-
121- const displayExt = `.${ keyword } ` ;
122-
123- if ( hasPlugins ) {
124- notificationManager . pushNotification ( {
125- title : formatString ( strings [ "extension recommendation title" ] , {
126- extension : displayExt ,
127- keyword : `mode:${ keyword } ` ,
128- } ) ,
129- message : formatString ( strings [ "extension recommendation message" ] , {
130- extension : displayExt ,
131- keyword : `mode:${ keyword } ` ,
132- } ) ,
133- icon : "extension" ,
134- type : "info" ,
135- action : ( ) => openExtensions ( `mode:${ keyword } ` ) ,
136- actions : [
137- {
138- text : strings [ "search plugins" ] ,
139- icon : "search" ,
140- action : ( ) => openExtensions ( `mode:${ keyword } ` ) ,
141- } ,
142- ] ,
143- } ) ;
144- return true ;
124+ if (
125+ ! shouldRecommendLanguageModeExtension ( filename , getModeForPath ( filename ) )
126+ ) {
127+ return false ;
145128 }
146129
147- const issueUrl = getIssueUrl ( keyword ) ;
130+ // An unknown extension is not enough evidence that the file contains a
131+ // programming language. Stay silent unless the registry has a matching
132+ // language-mode plugin to recommend.
133+ if ( ! hasPlugins ) return false ;
134+
135+ const displayExt = `.${ keyword } ` ;
148136 notificationManager . pushNotification ( {
149- title : formatString ( strings [ "extension request title" ] , {
137+ title : formatString ( strings [ "extension recommendation title" ] , {
150138 extension : displayExt ,
151- keyword,
139+ keyword : `mode: ${ keyword } ` ,
152140 } ) ,
153- message : formatString ( strings [ "extension request message" ] , {
141+ message : formatString ( strings [ "extension recommendation message" ] , {
154142 extension : displayExt ,
155- keyword,
143+ keyword : `mode: ${ keyword } ` ,
156144 } ) ,
157145 icon : "extension" ,
158- type : "warning " ,
159- action : ( ) => openUrl ( issueUrl ) ,
146+ type : "info " ,
147+ action : ( ) => openExtensions ( `mode: ${ keyword } ` ) ,
160148 actions : [
161149 {
162- text : strings [ "request plugin " ] ,
163- icon : "open_in_new " ,
164- action : ( ) => openUrl ( issueUrl ) ,
150+ text : strings [ "search plugins " ] ,
151+ icon : "search " ,
152+ action : ( ) => openExtensions ( `mode: ${ keyword } ` ) ,
165153 } ,
166154 ] ,
167155 } ) ;
0 commit comments