mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Add 'triggerCharacter' option for completions requests (#23491)
* Add 'triggerCharacter' option for completions requests * mhegazy code review * More code review * Handle '<' in comment
This commit is contained in:
@@ -842,6 +842,7 @@ namespace FourSlash {
|
||||
|
||||
const actualCompletions = this.getCompletionListAtCaret(options);
|
||||
if (!actualCompletions) {
|
||||
if (expected === undefined) return;
|
||||
this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
|
||||
}
|
||||
|
||||
@@ -4653,10 +4654,12 @@ namespace FourSlashInterface {
|
||||
|
||||
export type ExpectedCompletionEntry = string | { name: string, insertText?: string, replacementSpan?: FourSlash.Range };
|
||||
export interface CompletionsAtOptions extends Partial<ts.UserPreferences> {
|
||||
triggerCharacter?: string;
|
||||
isNewIdentifierLocation?: boolean;
|
||||
}
|
||||
|
||||
export interface VerifyCompletionListContainsOptions extends ts.UserPreferences {
|
||||
triggerCharacter?: string;
|
||||
sourceDisplay: string;
|
||||
isRecommended?: true;
|
||||
insertText?: string;
|
||||
|
||||
@@ -1749,6 +1749,7 @@ namespace ts.server.protocol {
|
||||
* Optional prefix to apply to possible completions.
|
||||
*/
|
||||
prefix?: string;
|
||||
triggerCharacter?: string;
|
||||
/**
|
||||
* @deprecated Use UserPreferences.includeCompletionsForModuleExports
|
||||
*/
|
||||
|
||||
@@ -1287,6 +1287,7 @@ namespace ts.server {
|
||||
|
||||
const completions = project.getLanguageService().getCompletionsAtPosition(file, position, {
|
||||
...this.getPreferences(file),
|
||||
triggerCharacter: args.triggerCharacter,
|
||||
includeExternalModuleExports: args.includeExternalModuleExports,
|
||||
includeInsertTextCompletions: args.includeInsertTextCompletions
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace ts.Completions {
|
||||
|
||||
const enum GlobalsSearch { Continue, Success, Fail }
|
||||
|
||||
export function getCompletionsAtPosition(host: LanguageServiceHost, program: Program, log: Log, sourceFile: SourceFile, position: number, preferences: UserPreferences): CompletionInfo | undefined {
|
||||
export function getCompletionsAtPosition(host: LanguageServiceHost, program: Program, log: Log, sourceFile: SourceFile, position: number, preferences: UserPreferences, triggerCharacter: string | undefined): CompletionInfo | undefined {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const compilerOptions = program.getCompilerOptions();
|
||||
if (isInReferenceComment(sourceFile, position)) {
|
||||
@@ -34,6 +34,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
const contextToken = findPrecedingToken(position, sourceFile);
|
||||
if (triggerCharacter && !isValidTrigger(sourceFile, triggerCharacter, contextToken, position)) return undefined;
|
||||
|
||||
if (isInString(sourceFile, position, contextToken)) {
|
||||
return !contextToken || !isStringLiteralLike(contextToken)
|
||||
@@ -2197,4 +2198,31 @@ namespace ts.Completions {
|
||||
function hasIndexSignature(type: Type): boolean {
|
||||
return !!type.getStringIndexType() || !!type.getNumberIndexType();
|
||||
}
|
||||
|
||||
function isValidTrigger(sourceFile: SourceFile, triggerCharacter: string, contextToken: Node, position: number): boolean {
|
||||
switch (triggerCharacter) {
|
||||
case '"':
|
||||
case "'":
|
||||
case "`":
|
||||
// Only automatically bring up completions if this is an opening quote.
|
||||
return isStringLiteralOrTemplate(contextToken) && position === contextToken.getStart(sourceFile) + 1;
|
||||
case "<":
|
||||
// Opening JSX tag
|
||||
return contextToken.kind === SyntaxKind.LessThanToken && contextToken.parent.kind !== SyntaxKind.BinaryExpression;
|
||||
default:
|
||||
return Debug.fail(triggerCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
function isStringLiteralOrTemplate(node: Node): node is StringLiteralLike | TemplateExpression | TaggedTemplateExpression {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.TemplateExpression:
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1409,7 +1409,8 @@ namespace ts {
|
||||
log,
|
||||
getValidSourceFile(fileName),
|
||||
position,
|
||||
fullPreferences);
|
||||
fullPreferences,
|
||||
options.triggerCharacter);
|
||||
}
|
||||
|
||||
function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = defaultPreferences): CompletionEntryDetails {
|
||||
|
||||
@@ -912,7 +912,7 @@ namespace ts {
|
||||
* to provide at the given source position and providing a member completion
|
||||
* list if requested.
|
||||
*/
|
||||
public getCompletionsAtPosition(fileName: string, position: number, preferences: UserPreferences | undefined) {
|
||||
public getCompletionsAtPosition(fileName: string, position: number, preferences: GetCompletionsAtPositionOptions | undefined) {
|
||||
return this.forwardJSONCall(
|
||||
`getCompletionsAtPosition('${fileName}', ${position}, ${preferences})`,
|
||||
() => this.languageService.getCompletionsAtPosition(fileName, position, preferences)
|
||||
|
||||
@@ -354,8 +354,9 @@ namespace ts {
|
||||
|
||||
export type OrganizeImportsScope = CombinedCodeFixScope;
|
||||
|
||||
/** @deprecated Use UserPreferences */
|
||||
export interface GetCompletionsAtPositionOptions extends UserPreferences {
|
||||
/** If the editor is asking for completions because a certain character was typed, and not because the user explicitly requested them, this should be set. */
|
||||
triggerCharacter?: string;
|
||||
/** @deprecated Use includeCompletionsForModuleExports */
|
||||
includeExternalModuleExports?: boolean;
|
||||
/** @deprecated Use includeCompletionsWithInsertText */
|
||||
|
||||
+3
-1
@@ -4455,8 +4455,9 @@ declare namespace ts {
|
||||
fileName: string;
|
||||
}
|
||||
type OrganizeImportsScope = CombinedCodeFixScope;
|
||||
/** @deprecated Use UserPreferences */
|
||||
interface GetCompletionsAtPositionOptions extends UserPreferences {
|
||||
/** If the editor is asking for completions because a certain character was typed, and not because the user explicitly requested them, this should be set. */
|
||||
triggerCharacter?: string;
|
||||
/** @deprecated Use includeCompletionsForModuleExports */
|
||||
includeExternalModuleExports?: boolean;
|
||||
/** @deprecated Use includeCompletionsWithInsertText */
|
||||
@@ -6667,6 +6668,7 @@ declare namespace ts.server.protocol {
|
||||
* Optional prefix to apply to possible completions.
|
||||
*/
|
||||
prefix?: string;
|
||||
triggerCharacter?: string;
|
||||
/**
|
||||
* @deprecated Use UserPreferences.includeCompletionsForModuleExports
|
||||
*/
|
||||
|
||||
+2
-1
@@ -4455,8 +4455,9 @@ declare namespace ts {
|
||||
fileName: string;
|
||||
}
|
||||
type OrganizeImportsScope = CombinedCodeFixScope;
|
||||
/** @deprecated Use UserPreferences */
|
||||
interface GetCompletionsAtPositionOptions extends UserPreferences {
|
||||
/** If the editor is asking for completions because a certain character was typed, and not because the user explicitly requested them, this should be set. */
|
||||
triggerCharacter?: string;
|
||||
/** @deprecated Use includeCompletionsForModuleExports */
|
||||
includeExternalModuleExports?: boolean;
|
||||
/** @deprecated Use includeCompletionsWithInsertText */
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @jsx: preserve
|
||||
|
||||
////const x: "a" | "b" = "/*openQuote*/"/*closeQuote*/;
|
||||
////const y: 'a' | 'b' = '/*openSingleQuote*/'/*closeSingleQuote*/;
|
||||
////const z: 'a' | 'b' = `/*openTemplate*/`/*closeTemplate*/;
|
||||
////const q: "`a`" | "`b`" = "`/*openTemplateInQuote*/a`/*closeTemplateInQuote*/";
|
||||
|
||||
////// "/*quoteInComment*/ </*lessInComment*/
|
||||
|
||||
// @Filename: /a.tsx
|
||||
////declare namespace JSX {
|
||||
//// interface Element {}
|
||||
//// interface IntrinsicElements {
|
||||
//// div: {};
|
||||
//// }
|
||||
////}
|
||||
////const ctr = </*openTag*/
|
||||
////const less = 1 </*lessThan*/
|
||||
|
||||
verify.completionsAt("openQuote", ["a", "b"], { triggerCharacter: '"' });
|
||||
verify.completionsAt("closeQuote", undefined, { triggerCharacter: '"' });
|
||||
|
||||
verify.completionsAt("openSingleQuote", ["a", "b"], { triggerCharacter: "'" });
|
||||
verify.completionsAt("closeSingleQuote", undefined, { triggerCharacter: "'" });
|
||||
|
||||
verify.completionsAt("openTemplate", ["a", "b"], { triggerCharacter: "`" });
|
||||
verify.completionsAt("closeTemplate", undefined, { triggerCharacter: "`" });
|
||||
|
||||
verify.completionsAt("openTemplateInQuote", undefined, { triggerCharacter: '`' });
|
||||
verify.completionsAt("closeTemplateInQuote", undefined, { triggerCharacter: '`' });
|
||||
|
||||
verify.completionsAt("quoteInComment", undefined, { triggerCharacter: '"' });
|
||||
verify.completionsAt("lessInComment", undefined, { triggerCharacter: "<" });
|
||||
|
||||
goTo.marker("openTag");
|
||||
verify.completionListContains("div", undefined, undefined, undefined, undefined, undefined, { triggerCharacter: "<" });
|
||||
verify.completionsAt("lessThan", undefined, { triggerCharacter: "<" });
|
||||
@@ -157,6 +157,7 @@ declare namespace FourSlashInterface {
|
||||
spanIndex?: number,
|
||||
hasAction?: boolean,
|
||||
options?: UserPreferences & {
|
||||
triggerCharacter?: string,
|
||||
sourceDisplay?: string,
|
||||
isRecommended?: true,
|
||||
insertText?: string,
|
||||
@@ -530,6 +531,7 @@ declare namespace FourSlashInterface {
|
||||
importModuleSpecifierPreference?: "relative" | "non-relative";
|
||||
}
|
||||
interface CompletionsAtOptions extends UserPreferences {
|
||||
triggerCharacter?: string;
|
||||
isNewIdentifierLocation?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user