Skip to content

Commit 502d22a

Browse files
committed
feat: Outline update! Now showing properties of type aliases and optionally first-based indexes of array literals and tuples!
eg: ```ts const a = {} as { foo: string // now in outline } // if outline.arraysTuplesNumberedItems is enabled (disabled by default) const a = [ "foo", // 0 in outline "bar" // 1 in outline ]; // the same for any tuples ```
1 parent 1746465 commit 502d22a

File tree

4 files changed

+57
-14
lines changed

4 files changed

+57
-14
lines changed

src/configurationType.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,16 @@ export type Configuration = {
322322
* Extend TypeScript outline!
323323
* Extend outline with:
324324
* - JSX Elements
325-
* more coming soon...
326-
* Should be stable enough!
325+
* - Type Alias Declarations
326+
* Should be stable!
327327
* @default false
328328
*/
329329
patchOutline: boolean
330+
/**
331+
* Recommended to enable if you use `patchOutline`
332+
* @default false
333+
*/
334+
'outline.arraysTuplesNumberedItems': boolean
330335
/**
331336
* Exclude covered strings/enum cases in switch in completions
332337
* @default true

src/extension.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ export const activateTsPlugin = (tsApi: { configurePlugin; onCompletionAccepted
2727
const syncConfig = () => {
2828
if (!tsApi) return
2929
console.log('sending configure request for typescript-essential-plugins')
30-
const config = vscode.workspace.getConfiguration().get(process.env.IDS_PREFIX!)
30+
const config: any = vscode.workspace.getConfiguration().get(process.env.IDS_PREFIX!)
3131

3232
tsApi.configurePlugin('typescript-essential-plugins', config)
3333

3434
if (process.env.PLATFORM === 'node') {
3535
// see comment in plugin
3636
require('fs').writeFileSync(
3737
require('path').join(extensionCtx.extensionPath, './plugin-config.json'),
38-
JSON.stringify(pickObj(config as Configuration, 'patchOutline')),
38+
JSON.stringify(pickObj(config, 'patchOutline', 'outline')),
3939
)
4040
}
4141

@@ -47,7 +47,11 @@ export const activateTsPlugin = (tsApi: { configurePlugin; onCompletionAccepted
4747
vscode.workspace.onDidChangeConfiguration(async ({ affectsConfiguration }) => {
4848
if (affectsConfiguration(process.env.IDS_PREFIX!)) {
4949
syncConfig()
50-
if (process.env.PLATFORM === 'node' && affectsConfiguration(getExtensionSettingId('patchOutline'))) {
50+
if (
51+
process.env.PLATFORM === 'node' &&
52+
(affectsConfiguration(getExtensionSettingId('patchOutline')) ||
53+
affectsConfiguration(getExtensionSettingId('outline.arraysTuplesNumberedItems')))
54+
) {
5155
await vscode.commands.executeCommand('typescript.restartTsServer')
5256
void vscode.window.showWarningMessage('Outline will be updated after text changes or window reload')
5357
}

typescript/src/decorateProxy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export const decorateLanguageService = (
139139
// so we forced to communicate via fs
140140
const config = JSON.parse(ts.sys.readFile(require('path').join(__dirname, '../../plugin-config.json'), 'utf8') ?? '{}')
141141
proxy.getNavigationTree = fileName => {
142-
if (c('patchOutline') || config.patchOutline) return getNavTreeItems(info, fileName)
142+
if (c('patchOutline') || config.patchOutline) return getNavTreeItems(info, fileName, config.outline)
143143
return languageService.getNavigationTree(fileName)
144144
}
145145
}

typescript/src/getPatchedNavTree.ts

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import { nodeModules } from './utils'
22
import * as semver from 'semver'
33
import { createLanguageService } from './dummyLanguageService'
44
import { getCannotFindCodes } from './utils/cannotFindCodes'
5+
import { Configuration } from './types'
56

67
// used at testing only
78
declare const __TS_SEVER_PATH__: string | undefined
89

9-
const getPatchedNavModule = (): { getNavigationTree(...args) } => {
10+
type AdditionalFeatures = Record<'arraysTuplesNumberedItems', boolean>
11+
12+
const getPatchedNavModule = (additionalFeatures: AdditionalFeatures): { getNavigationTree(...args) } => {
1013
// what is happening here: grabbing & patching NavigationBar module contents from actual running JS
1114
const tsServerPath = typeof __TS_SEVER_PATH__ !== 'undefined' ? __TS_SEVER_PATH__ : require.main!.filename
1215
// current lib/tsserver.js
@@ -23,30 +26,60 @@ const getPatchedNavModule = (): { getNavigationTree(...args) } => {
2326
linesOffset: number
2427
addString?: string
2528
removeLines?: number
29+
// transform?: (found: string, content: string, position: number) => [string?, string?]
2630
}
2731

2832
const patchLocations: PatchLocation[] = [
2933
{
3034
searchString: 'function addChildrenRecursively(node)',
3135
linesOffset: 7,
32-
addString: `
36+
addString: /* js */ `
3337
case ts.SyntaxKind.JsxSelfClosingElement:
3438
addLeafNode(node)
3539
break;
3640
case ts.SyntaxKind.JsxElement:
3741
startNode(node)
3842
ts.forEachChild(node, addChildrenRecursively);
3943
endNode()
40-
break`,
44+
break;`,
4145
},
46+
{
47+
searchString: 'case 262 /* SyntaxKind.TypeAliasDeclaration */',
48+
linesOffset: 3,
49+
// https://github.com/microsoft/TypeScript/pull/52558/
50+
addString: /* js */ `
51+
case ts.SyntaxKind.TypeAliasDeclaration:
52+
addNodeWithRecursiveChild(node, node.type);
53+
break;
54+
`,
55+
},
56+
{
57+
searchString: 'case 262 /* SyntaxKind.TypeAliasDeclaration */',
58+
linesOffset: 0,
59+
removeLines: 1,
60+
},
61+
// prettier-ignore
62+
...additionalFeatures.arraysTuplesNumberedItems ? [{
63+
searchString: 'function addChildrenRecursively(node)',
64+
linesOffset: 7,
65+
addString: /* js */ `
66+
case ts.SyntaxKind.TupleType:
67+
case ts.SyntaxKind.ArrayLiteralExpression:
68+
const { elements } = node;
69+
for (const [i, element] of elements.entries()) {
70+
addNodeWithRecursiveChild(element, element, ts.setTextRange(ts.factory.createIdentifier(i.toString()), element));
71+
}
72+
break;
73+
`,
74+
}] : [],
4275
{
4376
searchString: 'return "<unknown>";',
4477
linesOffset: -1,
45-
addString: `
78+
addString: /* js */ `
4679
case ts.SyntaxKind.JsxSelfClosingElement:
47-
return getNameFromJsxTag(node);
80+
return getNameFromJsxTag(node);
4881
case ts.SyntaxKind.JsxElement:
49-
return getNameFromJsxTag(node.openingElement);`,
82+
return getNameFromJsxTag(node.openingElement);`,
5083
},
5184
]
5285

@@ -131,10 +164,11 @@ const getPatchedNavModule = (): { getNavigationTree(...args) } => {
131164

132165
let navModule: { getNavigationTree: any }
133166

134-
export const getNavTreeItems = (info: ts.server.PluginCreateInfo, fileName: string) => {
135-
if (!navModule) navModule = getPatchedNavModule()
167+
export const getNavTreeItems = (info: ts.server.PluginCreateInfo, fileName: string, additionalFeatures: AdditionalFeatures) => {
168+
if (!navModule) navModule = getPatchedNavModule(additionalFeatures)
136169
const program = info.languageService.getProgram()
137170
if (!program) throw new Error('no program')
171+
// sourceFile is unreliable to use here, as it's not the same as the one used within original getNavigationTree
138172
const sourceFile = program?.getSourceFile(fileName)
139173
if (!sourceFile) throw new Error('no sourceFile')
140174

0 commit comments

Comments
 (0)