Skip to content

Commit 1ff56ba

Browse files
committed
feat: provide snippets for attribute and method completions
when the user selects the attribute in the completion list, the position of the cursor is not expected. It should be in the middle of quotes.
1 parent b5c5b2d commit 1ff56ba

File tree

1 file changed

+75
-3
lines changed

1 file changed

+75
-3
lines changed

server/src/completion.ts

+75-3
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,81 @@ export function tsCompletionEntryToLspCompletionItem(
7777
// Text that actually gets inserted to the document. It could be different
7878
// from 'entry.name'. For example, a method name could be 'greet', but the
7979
// insertText is 'greet()'.
80-
const insertText = entry.insertText || entry.name;
81-
item.textEdit = entry.replacementSpan ?
82-
lsp.TextEdit.replace(tsTextSpanToLspRange(scriptInfo, entry.replacementSpan), insertText) :
80+
let insertText = entry.insertText || entry.name;
81+
let replaceSpan = entry.replacementSpan;
82+
if (kind === CompletionKind.attribute) {
83+
// <div [(ng|)]=></div>
84+
// ^-------------- start
85+
// ^------- end
86+
let start = scriptInfo.lineOffsetToPosition(position.line + 1, position.character) - 1;
87+
let end = scriptInfo.lineOffsetToPosition(position.line + 1, position.character) + 4;
88+
if (replaceSpan) {
89+
start = replaceSpan.start - 2;
90+
end = replaceSpan.start + replaceSpan.length + 3;
91+
}
92+
const attributeText = scriptInfo.getSnapshot().getText(start, end);
93+
switch (attributeText[1]) {
94+
case '*':
95+
case '-':
96+
// *ngI| => *ngIf="|"
97+
// on-cli| => on-click="|"
98+
if (attributeText[attributeText.length - 3] !== '=') {
99+
item.insertTextFormat = lsp.InsertTextFormat.Snippet;
100+
insertText = `${insertText}="\${0}"`;
101+
replaceSpan = {start: start + 2, length: end - start - 5};
102+
}
103+
break;
104+
case '[':
105+
// [ngMo|] => [ngModel]="|"
106+
if (attributeText[attributeText.length - 2] !== '=') {
107+
item.insertTextFormat = lsp.InsertTextFormat.Snippet;
108+
insertText = `${insertText}]="\${0}"`;
109+
replaceSpan = {start: start + 2, length: end - start - 4};
110+
}
111+
break;
112+
case '(':
113+
if (attributeText[0] === '[') {
114+
// [(ngMod|)] => [(ngModel)]="|"
115+
if (attributeText[attributeText.length - 1] !== '=') {
116+
item.insertTextFormat = lsp.InsertTextFormat.Snippet;
117+
insertText = `${insertText})]="\${0}"`;
118+
replaceSpan = {start: start + 2, length: end - start - 3};
119+
}
120+
} else {
121+
// (clic|) => (click)="|"
122+
if (attributeText[attributeText.length - 2] !== '=') {
123+
item.insertTextFormat = lsp.InsertTextFormat.Snippet;
124+
insertText = `${insertText})="\${0}"`;
125+
replaceSpan = {start: start + 2, length: end - start - 4};
126+
}
127+
}
128+
break;
129+
default:
130+
break;
131+
}
132+
}
133+
134+
if (kind === CompletionKind.method) {
135+
let start = scriptInfo.lineOffsetToPosition(position.line + 1, position.character) + 1;
136+
let end = scriptInfo.lineOffsetToPosition(position.line + 1, position.character) + 2;
137+
if (replaceSpan) {
138+
start = replaceSpan.start;
139+
end = replaceSpan.start + replaceSpan.length + 1;
140+
}
141+
const methodText = scriptInfo.getSnapshot().getText(start, end);
142+
if (methodText.endsWith('(')) {
143+
// meth|() => method(|)
144+
replaceSpan = {start: start, length: end - start};
145+
insertText = insertText.slice(0, insertText.length - 1) + '\${0}';
146+
} else {
147+
// meth| => method(|)
148+
insertText = insertText.slice(0, insertText.length - 1) + '\${0})';
149+
}
150+
item.insertTextFormat = lsp.InsertTextFormat.Snippet;
151+
}
152+
153+
item.textEdit = replaceSpan ?
154+
lsp.TextEdit.replace(tsTextSpanToLspRange(scriptInfo, replaceSpan), insertText) :
83155
lsp.TextEdit.insert(position, insertText);
84156
return item;
85157
}

0 commit comments

Comments
 (0)