Skip to content

Commit e14c252

Browse files
committed
feat: Big features! Hide or change sorting of auto-imports. Configurable per specific module and symbol name!
1 parent 08952ab commit e14c252

File tree

7 files changed

+373
-40
lines changed

7 files changed

+373
-40
lines changed

README.MD

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,38 @@ const a = 5
179179
a = 6
180180
```
181181

182+
## Auto Imports
183+
184+
With this plugin you have total (almost) control over auto imports that appear in completions, quick fixes and import all quick fix. Some examples of what you can do:
185+
186+
- I never want to see `join` suggestion from `path/win32` module (it's a Node.js [module](https://nodejs.org/dist/latest-v18.x/docs/api/path.html#pathwin32), defined via module augmentation)
187+
- I never want to see any suggestions from `path/*` modules
188+
- I always want `join` to be imported from `path/posix` module on window (also would appear first in completions & single quick fix)
189+
- I always want `useState` to be imported from local files first
190+
191+
Some settings examples:
192+
193+
<!-- just duplicated from configurationType -->
194+
195+
```jsonc
196+
"suggestions.ignoreAutoImport": [
197+
"path", // ignore path, but not path/posix or path/win32 modules
198+
"path/*", // ignore path, path/posix and path/win32
199+
"path/*#join", // ignore path, path/posix and path/win32, but only join symbol
200+
"path/*#join,resolve", // ignore path, path/posix and path/win32, but only join and resolve symbol
201+
],
202+
"autoImport.changeSorting": {
203+
"join": ["path/posix"], // other suggestions will be below
204+
"resolve": ["*", "path/posix"], // make `path/posix` appear below any other suggestions
205+
"useState": ["."], // `.` (dot) is reserved for local suggestions, which makes them appear above other
206+
},
207+
// for some even more specific cases?
208+
// "autoImport.alwaysIgnoreInImportAll": ["lodash"] // exclude all possible imports from import all request
209+
```
210+
211+
> Note: changeSorting might not preserve sorting of other existing suggestions which not defined by rules, there is WIP
212+
> Also I'm thinking of making it learn and syncing of most-used imports automatically
213+
182214
## Special Commands List
183215

184216
### Go to / Select Nodes by Kind

src/configurationType.ts

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,31 @@ export type Configuration = {
5656
*/
5757
'patchToString.enable': boolean
5858
/**
59+
* Format of this setting is very close to `jsxCompletionsMap` setting:
60+
* `path#symbol` (exact) or `path/*#symbol` (`#symbol` part can be ommited)
61+
*
5962
* Note: Please use `javascript`/`typescript.preferences.autoImportFileExcludePatterns` when possible, to achieve better performance!
63+
*
6064
* e.g. instead of declaring `@mui/icons-material` here, declare `node_modules/@mui/icons-material` in aforementioned setting.
6165
*
6266
* And only use this, if auto-imports coming not from physical files (e.g. some modules node imports)
67+
*
68+
* Examples:
69+
* - `path` - ignore path, but not path/posix or path/win32 modules
70+
* - `path/*` - ignore path, path/posix and path/win32
71+
* - `path/*#join` - ignore path, path/posix and path/win32, but only join symbol
72+
* - `path/*#join,resolve` - ignore path, path/posix and path/win32, but only join and resolve symbol
73+
*
74+
* - jquery/* - ignore absolutely all auto imports from jquery, even if it was declared virtually (declare module)
6375
* @default []
6476
*/
65-
'suggestions.banAutoImportPackages': string[]
77+
'suggestions.ignoreAutoImport': string[]
78+
/**
79+
* Disable it only if it causes problems / crashes with TypeScript, which of course should never happen
80+
* But it wasn't tested on very old versions
81+
* @default false
82+
*/
83+
// 'advanced.disableAutoImportCodeFixPatching': boolean
6684
/**
6785
* What insert text to use for keywords (e.g. `return`)
6886
* @default space
@@ -366,15 +384,22 @@ export type Configuration = {
366384
*/
367385
'experiments.excludeNonJsxCompletions': boolean
368386
/**
369-
* Format of this setting is very close to `jsxCompletionsMap` setting:
370-
* path#symbol
371-
* Examples (keys in object):
372-
* #join - adjust `join` symbol from every module
373-
* path/win32#join - adjust `join` symbol from from path/win32 module
374-
* path/
375-
* Adjust actions (values in object):
376-
* false - completely disable symbol(s) suggesting
377-
* array of strings - change sorting of - first takes precedence for auto-imports (code actions & suggestions)
378-
*/
379-
'autoImport.adjustBySymbol': { ['path#symbol'] }
387+
* Map *symbol - array of modules* to change sorting of imports - first available takes precedence in auto import code fixes (+ import all action)
388+
*
389+
* Examples:
390+
* - `join`: ['path/posix'] // other suggestions will be below
391+
* - `resolve`: ['*', 'path/posix'] // make `path/posix` appear below any other suggestions
392+
* - `useEventListener`: ['.'] // `.` (dot) is reserved for local suggestions, which makes them appear above other
393+
* @default {}
394+
*/
395+
'autoImport.changeSorting': { [pathAndOrSymbol: string]: string[] }
396+
/**
397+
* Advanced. Use `suggestions.ignoreAutoImport` setting if possible.
398+
*
399+
* Packages to ignore in import all fix.
400+
*
401+
* TODO syntaxes /* and module#symbol unsupported (easy)
402+
* @default []
403+
*/
404+
'autoImport.alwaysIgnoreInImportAll': string[]
380405
}

src/migrateSettings.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ export default () => {
1717
mustBePrimitive: true,
1818
},
1919
},
20+
{
21+
rename: {
22+
old: 'suggestions.banAutoImportPackages',
23+
new: 'suggestions.ignoreAutoImport',
24+
mustBePrimitive: false,
25+
},
26+
},
2027
],
2128
process.env.IDS_PREFIX!,
2229
)

typescript/src/adjustAutoImports.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { GetConfig } from './types'
2+
import { addObjectMethodResultInterceptors, findChildContainingExactPosition } from './utils'
3+
4+
let currentSymbolName: string | undefined
5+
6+
interface ParsedIgnoreSetting {
7+
module: string
8+
symbols: string[]
9+
isAnySymbol: boolean
10+
moduleCompare: 'startsWith' | 'strict'
11+
}
12+
13+
// will be removed once I'm sure performance can't be improved
14+
const initIgnoreAutoImport = () => {
15+
addObjectMethodResultInterceptors(tsFull, {
16+
// todo
17+
createPackageJsonImportFilter(res, fromFile) {
18+
return {
19+
...res,
20+
allowsImportingAmbientModule(moduleSymbol, moduleSpecifierResolutionHost) {
21+
return true
22+
// return isModuleShouldBeIgnored(moduleSymbol.name.slice(1, -1), '')
23+
},
24+
// allowsImportingSourceFile(sourceFile, moduleSpecifierResolutionHost) {
25+
// return false
26+
// },
27+
allowsImportingSpecifier(moduleSpecifier) {
28+
const result = res.allowsImportingSpecifier(moduleSpecifier)
29+
if (!result) return false
30+
return false
31+
},
32+
}
33+
},
34+
})
35+
// addObjectMethodResultInterceptors(tsFull.codefix, {
36+
// getAllFixes(res) {
37+
// return res
38+
// },
39+
// })
40+
}
41+
42+
export const getIgnoreAutoImportSetting = (c: GetConfig) => {
43+
return c('suggestions.ignoreAutoImport').map((spec): ParsedIgnoreSetting => {
44+
const hashIndex = spec.indexOf('#')
45+
let module = hashIndex === -1 ? spec : spec.slice(0, hashIndex)
46+
const moduleCompare = module.endsWith('/*') ? 'startsWith' : 'strict'
47+
if (moduleCompare === 'startsWith') {
48+
module = module.slice(0, -'/*'.length)
49+
}
50+
if (hashIndex === -1) {
51+
return {
52+
module,
53+
symbols: [],
54+
isAnySymbol: true,
55+
moduleCompare,
56+
}
57+
}
58+
const symbolsString = spec.slice(hashIndex + 1)
59+
// * (glob asterisk) is reserved for future ussage
60+
const isAnySymbol = symbolsString === '*'
61+
return {
62+
module,
63+
symbols: isAnySymbol ? [] : symbolsString.split(','),
64+
isAnySymbol,
65+
moduleCompare,
66+
}
67+
})
68+
}
69+
70+
export const isAutoImportEntryShouldBeIgnored = (ignoreAutoImportSetting: ParsedIgnoreSetting[], targetModule: string, symbol: string) => {
71+
for (const { module, moduleCompare, isAnySymbol, symbols } of ignoreAutoImportSetting) {
72+
const isIgnoreModule = moduleCompare === 'startsWith' ? targetModule.startsWith(module) : targetModule === module
73+
if (!isIgnoreModule) continue
74+
if (isAnySymbol) return true
75+
if (!symbols.includes(symbol)) continue
76+
return true
77+
}
78+
return false
79+
}
80+
81+
export const shouldChangeSortingOfAutoImport = (symbolName: string, c: GetConfig) => {
82+
const arr = c('autoImport.changeSorting')[symbolName]
83+
return arr && arr.length > 0
84+
}
85+
86+
export const changeSortingOfAutoImport = (c: GetConfig, symbolName: string): ((module: string) => number) => {
87+
const arr = c('autoImport.changeSorting')[symbolName]
88+
if (!arr || !arr.length) return () => 0
89+
const maxIndex = arr.length
90+
return module => {
91+
let actualIndex = arr.findIndex(x => {
92+
return x.endsWith('/*') ? module.startsWith(x.slice(0, -'/*'.length)) : module === x
93+
})
94+
// . - index, don't treat some node_modules-ignored modules as local such as `.vite`
95+
const isLocal = module.startsWith('./') || module.startsWith('../') || module === '.'
96+
if (actualIndex === -1) actualIndex = arr.findIndex(x => (isLocal ? x === '.' : x === '*'))
97+
return actualIndex === -1 ? maxIndex : actualIndex
98+
}
99+
}

0 commit comments

Comments
 (0)