Skip to content

Commit 2ea61a7

Browse files
committed
Plugin proposal
1 parent 8dc3b2e commit 2ea61a7

File tree

2 files changed

+179
-1
lines changed

2 files changed

+179
-1
lines changed

src/services/plugins.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/// <reference path="..\compiler\program.ts"/>
2+
/// <reference path="services.ts"/>
3+
4+
namespace ts {
5+
6+
// LanguageServicePlugin is an interface that plugins can implement to replace, filter, or augment
7+
// results returned by the TypeScript language service to the language service host.
8+
9+
export interface LanguageServicePlugin {
10+
// Overrides
11+
12+
// A plugin can implement one of the override methods to replace the results that would
13+
// be returned by the TypeScript language service. If a plugin returns a defined results
14+
// (that is, is not undefined) then that result is used instead of invoking the
15+
// corresponding TypeScript method. If multiple plugins are registered, they are
16+
// consulted in the order they are returned from the host. The first defined result
17+
// returned by a plugin is used and no other plugin overrides are consulted.
18+
19+
getOptionsDiagnostics?(): Diagnostic[];
20+
getSyntacticDiagnostics?(fileName: string): Diagnostic[];
21+
getSemanticDiagnostics?(fileName: string): Diagnostic[];
22+
getEncodedSyntacticClassifications?(fileName: string, span: TextSpan): Classifications;
23+
getEncodedSemanticClassifications?(fileName: string, span: TextSpan): Classifications;
24+
getCompletionsAtPosition?(fileName: string, position: number): CompletionInfo;
25+
getCompletionEntryDetails?(fileName: string, position: number, entryName: string): CompletionEntryDetails;
26+
getQuickInfoAtPosition?(fileName: string, position: number): QuickInfo;
27+
getNameOrDottedNameSpan?(fileName: string, startPos: number, endPos: number): TextSpan;
28+
getBreakpointStatementAtPosition?(fileName: string, position: number): TextSpan;
29+
getSignatureHelpItems?(fileName: string, position: number): SignatureHelpItems;
30+
getRenameInfo?(fileName: string, position: number): RenameInfo;
31+
findRenameLocations?(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[];
32+
getDefinitionAtPosition?(fileName: string, position: number): DefinitionInfo[];
33+
getTypeDefinitionAtPosition?(fileName: string, position: number): DefinitionInfo[];
34+
getReferencesAtPosition?(fileName: string, position: number): ReferenceEntry[];
35+
findReferences?(fileName: string, position: number): ReferencedSymbol[];
36+
getDocumentHighlights?(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[];
37+
getNavigateToItems?(searchValue: string, maxResultCount: number): NavigateToItem[];
38+
getNavigationBarItems?(fileName: string): NavigationBarItem[];
39+
getOutliningSpans?(fileName: string): OutliningSpan[];
40+
getTodoComments?(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
41+
getBraceMatchingAtPosition?(fileName: string, position: number): TextSpan[];
42+
getIndentationAtPosition?(fileName: string, position: number, options: EditorOptions): number;
43+
getFormattingEditsForRange?(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[];
44+
getFormattingEditsForDocument?(fileName: string, options: FormatCodeOptions): TextChange[];
45+
getFormattingEditsAfterKeystroke?(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[];
46+
getDocCommentTemplateAtPosition?(fileName: string, position: number): TextInsertion;
47+
getSourceFile?(fileName: string): SourceFile;
48+
49+
// Filters
50+
51+
// A plugin can implement one of the filter methods to augment, extend or modify a result
52+
// prior to the host receiving it. The TypeScript language service is invoked and the
53+
// result is passed to the plugin as the value of the previous parameter. If more than one
54+
// plugin is registered, the plugins are consulted in the order they are returned from the
55+
// host. The value passed in as previous is the result returned by the prior plugin. If a
56+
// plugin returns undefined, the result passed in as previous is used and the undefined
57+
// result is ignored. All plugins are consulted before the result is returned to the host.
58+
// If a plugin overrides behavior of the method, no filter methods are consulted.
59+
60+
getOptionsDiagnosticsFilter?(previous: Diagnostic[]): Diagnostic[];
61+
getSyntacticDiagnosticsFilter?(fileName: string, previous: Diagnostic[]): Diagnostic[];
62+
getSemanticDiagnosticsFilter?(fileName: string, previous: Diagnostic[]): Diagnostic[];
63+
getEncodedSyntacticClassificationsFilter?(fileName: string, span: TextSpan, previous: Classifications): Classifications;
64+
getEncodedSemanticClassificationsFilter?(fileName: string, span: TextSpan, previous: Classifications): Classifications;
65+
getCompletionsAtPositionFilter?(fileName: string, position: number, previous: CompletionInfo): CompletionInfo;
66+
getCompletionEntryDetailsFilter?(fileName: string, position: number, entryName: string, previous: CompletionEntryDetails): CompletionEntryDetails;
67+
getQuickInfoAtPositionFilter?(fileName: string, position: number, previous: QuickInfo): QuickInfo;
68+
getNameOrDottedNameSpanFilter?(fileName: string, startPos: number, endPos: number, previous: TextSpan): TextSpan;
69+
getBreakpointStatementAtPositionFilter?(fileName: string, position: number, previous: TextSpan): TextSpan;
70+
getSignatureHelpItemsFilter?(fileName: string, position: number, previous: SignatureHelpItems): SignatureHelpItems;
71+
getRenameInfoFilter?(fileName: string, position: number, previous: RenameInfo): RenameInfo;
72+
findRenameLocationsFilter?(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, previous: RenameLocation[]): RenameLocation[];
73+
getDefinitionAtPositionFilter?(fileName: string, position: number, previous: DefinitionInfo[]): DefinitionInfo[];
74+
getTypeDefinitionAtPositionFilter?(fileName: string, position: number, previous: DefinitionInfo[]): DefinitionInfo[];
75+
getReferencesAtPositionFilter?(fileName: string, position: number, previous: ReferenceEntry[]): ReferenceEntry[];
76+
findReferencesFilter?(fileName: string, position: number, previous: ReferencedSymbol[]): ReferencedSymbol[];
77+
getDocumentHighlightsFilter?(fileName: string, position: number, filesToSearch: string[], previous: DocumentHighlights[]): DocumentHighlights[];
78+
getNavigateToItemsFilter?(searchValue: string, maxResultCount: number, previous: NavigateToItem[]): NavigateToItem[];
79+
getNavigationBarItemsFilter?(fileName: string, previous: NavigationBarItem[]): NavigationBarItem[];
80+
getOutliningSpansFilter?(fileName: string, previous: OutliningSpan[]): OutliningSpan[];
81+
getTodoCommentsFilter?(fileName: string, descriptors: TodoCommentDescriptor[], previous: TodoComment[]): TodoComment[];
82+
getBraceMatchingAtPositionFilter?(fileName: string, position: number, previous: TextSpan[]): TextSpan[];
83+
getIndentationAtPositionFilter?(fileName: string, position: number, options: EditorOptions, previous: number): number;
84+
getFormattingEditsForRangeFilter?(fileName: string, start: number, end: number, options: FormatCodeOptions, previous: TextChange[]): TextChange[];
85+
getFormattingEditsForDocumentFilter?(fileName: string, options: FormatCodeOptions, previous: TextChange[]): TextChange[];
86+
getFormattingEditsAfterKeystrokeFilter?(fileName: string, position: number, key: string, options: FormatCodeOptions, previous: TextChange[]): TextChange[];
87+
getDocCommentTemplateAtPositionFilter?(fileName: string, position: number, previous: TextInsertion): TextInsertion;
88+
getSourceFileFilter?(fileName: string, previous: SourceFile): SourceFile;
89+
}
90+
91+
// The LanguageServiceHost interface is extended to allow a host to supply a list of plugins
92+
// to be consulted.
93+
export interface LanguageServiceHost {
94+
getPlugins?(service: LanguageService): LanguageServicePlugin[];
95+
}
96+
97+
// A factory used by the host to create the plugins retuned by getPlugins(). The service
98+
// instance passed into the factory will not consult plugins before producing a result and,
99+
// therefore, can be used to determine what an unaugmented version of the TypeScript language
100+
// service would return for the call being overriden. It also guarentees not to cause indirect
101+
// recursion involving the plugin.
102+
export interface LanguageServicePluginFactory {
103+
create(service: LanguageService, registry: DocumentRegistry): LanguageServicePlugin;
104+
}
105+
}

src/services/services.ts

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/// <reference path='navigateTo.ts' />
66
/// <reference path='navigationBar.ts' />
77
/// <reference path='patternMatcher.ts' />
8+
/// <reference path='plugins.ts' />
89
/// <reference path='signatureHelp.ts' />
910
/// <reference path='utilities.ts' />
1011
/// <reference path='jsTyping.ts' />
@@ -7581,7 +7582,7 @@ namespace ts {
75817582
}
75827583
}
75837584

7584-
return {
7585+
let service: LanguageService = {
75857586
dispose,
75867587
cleanupSemanticCache,
75877588
getSyntacticDiagnostics,
@@ -7619,6 +7620,78 @@ namespace ts {
76197620
getNonBoundSourceFile,
76207621
getProgram
76217622
};
7623+
7624+
const plugins = host.getPlugins && host.getPlugins(service);
7625+
if (plugins && plugins.length) {
7626+
function wrap<T extends Function>(name: string, method: T): T {
7627+
const overrides = plugins
7628+
.map(plugin => ({plugin, f: <Function>(<any>plugin)[name]}))
7629+
.filter(p => !!p.f);
7630+
const filters = plugins
7631+
.map(plugin => ({plugin, f: <Function>(<any>plugin)[name + "Filter"]}))
7632+
.filter(p => !!p.f);
7633+
if (overrides.length || filter.length) {
7634+
return <T><any>function() {
7635+
for (const p of overrides) {
7636+
const result = p.f.apply(p.plugin, arguments);
7637+
if (result) {
7638+
return result;
7639+
}
7640+
}
7641+
let result = method.apply(this, arguments);
7642+
for (const p of filters) {
7643+
const args = Array.prototype.slice.call(arguments, 0);
7644+
args.push(result);
7645+
const r = p.f.apply(p.plugin, args);
7646+
if (r)
7647+
result = r;
7648+
}
7649+
return result;
7650+
};
7651+
}
7652+
return method;
7653+
}
7654+
service = {
7655+
dispose,
7656+
cleanupSemanticCache,
7657+
getSyntacticDiagnostics: wrap("getSyntacticDiagnostics", getSyntacticDiagnostics),
7658+
getSemanticDiagnostics: wrap("getSemanticDiagnostics", getSemanticDiagnostics),
7659+
getCompilerOptionsDiagnostics: wrap("getCompilerOptionsDiagnostics", getCompilerOptionsDiagnostics),
7660+
getSyntacticClassifications: wrap("getSyntacticClassifications", getSyntacticClassifications),
7661+
getSemanticClassifications: wrap("getSemanticClassifications", getSemanticClassifications),
7662+
getEncodedSyntacticClassifications: wrap("getEncodedSyntacticClassifications", getEncodedSyntacticClassifications),
7663+
getEncodedSemanticClassifications: wrap("getEncodedSemanticClassifications", getEncodedSemanticClassifications),
7664+
getCompletionsAtPosition: wrap("getCompletionsAtPosition", getCompletionsAtPosition),
7665+
getCompletionEntryDetails: wrap("getCompletionEntryDetails", getCompletionEntryDetails),
7666+
getSignatureHelpItems: wrap("getSignatureHelpItems", getSignatureHelpItems),
7667+
getQuickInfoAtPosition: wrap("getQuickInfoAtPosition", getQuickInfoAtPosition),
7668+
getDefinitionAtPosition: wrap("getDefinitionAtPosition", getDefinitionAtPosition),
7669+
getTypeDefinitionAtPosition: wrap("getTypeDefinitionAtPosition", getTypeDefinitionAtPosition),
7670+
getReferencesAtPosition: wrap("getReferencesAtPosition", getReferencesAtPosition),
7671+
findReferences: wrap("findReferences", findReferences),
7672+
getOccurrencesAtPosition: wrap("getOccurrencesAtPosition", getOccurrencesAtPosition),
7673+
getDocumentHighlights: wrap("getDocumentHighlights", getDocumentHighlights),
7674+
getNameOrDottedNameSpan: wrap("getNameOrDottedNameSpan", getNameOrDottedNameSpan),
7675+
getBreakpointStatementAtPosition: wrap("getBreakpointStatementAtPosition", getBreakpointStatementAtPosition),
7676+
getNavigateToItems: wrap("getNavigateToItems", getNavigateToItems),
7677+
getRenameInfo: wrap("getRenameInfo", getRenameInfo),
7678+
findRenameLocations: wrap("findRenameLocations", findRenameLocations),
7679+
getNavigationBarItems: wrap("getNavigationBarItems", getNavigationBarItems),
7680+
getOutliningSpans: wrap("getOutliningSpans", getOutliningSpans),
7681+
getTodoComments: wrap("getTodoComments", getTodoComments),
7682+
getBraceMatchingAtPosition: wrap("getBraceMatchingAtPosition", getBraceMatchingAtPosition),
7683+
getIndentationAtPosition: wrap("getIndentationAtPosition", getIndentationAtPosition),
7684+
getFormattingEditsForRange: wrap("getFormattingEditsForRange", getFormattingEditsForRange),
7685+
getFormattingEditsForDocument: wrap("getFormattingEditsForDocument", getFormattingEditsForDocument),
7686+
getFormattingEditsAfterKeystroke: wrap("getFormattingEditsAfterKeystroke", getFormattingEditsAfterKeystroke),
7687+
getDocCommentTemplateAtPosition: wrap("getDocCommentTemplateAtPosition", getDocCommentTemplateAtPosition),
7688+
getEmitOutput,
7689+
getSourceFile,
7690+
getProgram
7691+
};
7692+
}
7693+
7694+
return service;
76227695
}
76237696

76247697
/* @internal */

0 commit comments

Comments
 (0)