in extensions/html-language-features/server/src/modes/javascriptMode.ts [27:313]
export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocumentRegions>): LanguageMode {
let jsDocuments = getLanguageModelCache<TextDocument>(10, 60, document => documentRegions.get(document).getEmbeddedDocument('javascript'));
let compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic };
let currentTextDocument: TextDocument;
let scriptFileVersion: number = 0;
function updateCurrentTextDocument(doc: TextDocument) {
if (!currentTextDocument || doc.uri !== currentTextDocument.uri || doc.version !== currentTextDocument.version) {
currentTextDocument = jsDocuments.get(doc);
scriptFileVersion++;
}
}
const host: ts.LanguageServiceHost = {
getCompilationSettings: () => compilerOptions,
getScriptFileNames: () => [FILE_NAME, jquery_d_ts],
getScriptKind: () => ts.ScriptKind.JS,
getScriptVersion: (fileName: string) => {
if (fileName === FILE_NAME) {
return String(scriptFileVersion);
}
return '1'; // default lib an jquery.d.ts are static
},
getScriptSnapshot: (fileName: string) => {
let text = '';
if (startsWith(fileName, 'vscode:')) {
if (fileName === FILE_NAME) {
text = currentTextDocument.getText();
}
} else {
text = ts.sys.readFile(fileName) || '';
}
return {
getText: (start, end) => text.substring(start, end),
getLength: () => text.length,
getChangeRange: () => undefined
};
},
getCurrentDirectory: () => '',
getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options)
};
let jsLanguageService = ts.createLanguageService(host);
let globalSettings: Settings = {};
return {
getId() {
return 'javascript';
},
doValidation(document: TextDocument): Diagnostic[] {
updateCurrentTextDocument(document);
const syntaxDiagnostics: ts.Diagnostic[] = jsLanguageService.getSyntacticDiagnostics(FILE_NAME);
const semanticDiagnostics = jsLanguageService.getSemanticDiagnostics(FILE_NAME);
return syntaxDiagnostics.concat(semanticDiagnostics).map((diag: ts.Diagnostic): Diagnostic => {
return {
range: convertRange(currentTextDocument, diag),
severity: DiagnosticSeverity.Error,
source: 'js',
message: ts.flattenDiagnosticMessageText(diag.messageText, '\n')
};
});
},
doComplete(document: TextDocument, position: Position): CompletionList {
updateCurrentTextDocument(document);
let offset = currentTextDocument.offsetAt(position);
let completions = jsLanguageService.getCompletionsAtPosition(FILE_NAME, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
if (!completions) {
return { isIncomplete: false, items: [] };
}
let replaceRange = convertRange(currentTextDocument, getWordAtText(currentTextDocument.getText(), offset, JS_WORD_REGEX));
return {
isIncomplete: false,
items: completions.entries.map(entry => {
return {
uri: document.uri,
position: position,
label: entry.name,
sortText: entry.sortText,
kind: convertKind(entry.kind),
textEdit: TextEdit.replace(replaceRange, entry.name),
data: { // data used for resolving item details (see 'doResolve')
languageId: 'javascript',
uri: document.uri,
offset: offset
}
};
})
};
},
doResolve(document: TextDocument, item: CompletionItem): CompletionItem {
updateCurrentTextDocument(document);
let details = jsLanguageService.getCompletionEntryDetails(FILE_NAME, item.data.offset, item.label, undefined, undefined, undefined);
if (details) {
item.detail = ts.displayPartsToString(details.displayParts);
item.documentation = ts.displayPartsToString(details.documentation);
delete item.data;
}
return item;
},
doHover(document: TextDocument, position: Position): Hover | null {
updateCurrentTextDocument(document);
let info = jsLanguageService.getQuickInfoAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
if (info) {
let contents = ts.displayPartsToString(info.displayParts);
return {
range: convertRange(currentTextDocument, info.textSpan),
contents: MarkedString.fromPlainText(contents)
};
}
return null;
},
doSignatureHelp(document: TextDocument, position: Position): SignatureHelp | null {
updateCurrentTextDocument(document);
let signHelp = jsLanguageService.getSignatureHelpItems(FILE_NAME, currentTextDocument.offsetAt(position), undefined);
if (signHelp) {
let ret: SignatureHelp = {
activeSignature: signHelp.selectedItemIndex,
activeParameter: signHelp.argumentIndex,
signatures: []
};
signHelp.items.forEach(item => {
let signature: SignatureInformation = {
label: '',
documentation: undefined,
parameters: []
};
signature.label += ts.displayPartsToString(item.prefixDisplayParts);
item.parameters.forEach((p, i, a) => {
let label = ts.displayPartsToString(p.displayParts);
let parameter: ParameterInformation = {
label: label,
documentation: ts.displayPartsToString(p.documentation)
};
signature.label += label;
signature.parameters!.push(parameter);
if (i < a.length - 1) {
signature.label += ts.displayPartsToString(item.separatorDisplayParts);
}
});
signature.label += ts.displayPartsToString(item.suffixDisplayParts);
ret.signatures.push(signature);
});
return ret;
}
return null;
},
findDocumentHighlight(document: TextDocument, position: Position): DocumentHighlight[] {
updateCurrentTextDocument(document);
const highlights = jsLanguageService.getDocumentHighlights(FILE_NAME, currentTextDocument.offsetAt(position), [FILE_NAME]);
const out: DocumentHighlight[] = [];
for (const entry of highlights || []) {
for (const highlight of entry.highlightSpans) {
out.push({
range: convertRange(currentTextDocument, highlight.textSpan),
kind: highlight.kind === 'writtenReference' ? DocumentHighlightKind.Write : DocumentHighlightKind.Text
});
}
}
return out;
},
findDocumentSymbols(document: TextDocument): SymbolInformation[] {
updateCurrentTextDocument(document);
let items = jsLanguageService.getNavigationBarItems(FILE_NAME);
if (items) {
let result: SymbolInformation[] = [];
let existing = Object.create(null);
let collectSymbols = (item: ts.NavigationBarItem, containerLabel?: string) => {
let sig = item.text + item.kind + item.spans[0].start;
if (item.kind !== 'script' && !existing[sig]) {
let symbol: SymbolInformation = {
name: item.text,
kind: convertSymbolKind(item.kind),
location: {
uri: document.uri,
range: convertRange(currentTextDocument, item.spans[0])
},
containerName: containerLabel
};
existing[sig] = true;
result.push(symbol);
containerLabel = item.text;
}
if (item.childItems && item.childItems.length > 0) {
for (let child of item.childItems) {
collectSymbols(child, containerLabel);
}
}
};
items.forEach(item => collectSymbols(item));
return result;
}
return [];
},
findDefinition(document: TextDocument, position: Position): Definition | null {
updateCurrentTextDocument(document);
let definition = jsLanguageService.getDefinitionAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
if (definition) {
return definition.filter(d => d.fileName === FILE_NAME).map(d => {
return {
uri: document.uri,
range: convertRange(currentTextDocument, d.textSpan)
};
});
}
return null;
},
findReferences(document: TextDocument, position: Position): Location[] {
updateCurrentTextDocument(document);
let references = jsLanguageService.getReferencesAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
if (references) {
return references.filter(d => d.fileName === FILE_NAME).map(d => {
return {
uri: document.uri,
range: convertRange(currentTextDocument, d.textSpan)
};
});
}
return [];
},
format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings): TextEdit[] {
currentTextDocument = documentRegions.get(document).getEmbeddedDocument('javascript', true);
scriptFileVersion++;
let formatterSettings = settings && settings.javascript && settings.javascript.format;
let initialIndentLevel = computeInitialIndent(document, range, formatParams);
let formatSettings = convertOptions(formatParams, formatterSettings, initialIndentLevel + 1);
let start = currentTextDocument.offsetAt(range.start);
let end = currentTextDocument.offsetAt(range.end);
let lastLineRange = null;
if (range.end.line > range.start.line && (range.end.character === 0 || isWhitespaceOnly(currentTextDocument.getText().substr(end - range.end.character, range.end.character)))) {
end -= range.end.character;
lastLineRange = Range.create(Position.create(range.end.line, 0), range.end);
}
let edits = jsLanguageService.getFormattingEditsForRange(FILE_NAME, start, end, formatSettings);
if (edits) {
let result = [];
for (let edit of edits) {
if (edit.span.start >= start && edit.span.start + edit.span.length <= end) {
result.push({
range: convertRange(currentTextDocument, edit.span),
newText: edit.newText
});
}
}
if (lastLineRange) {
result.push({
range: lastLineRange,
newText: generateIndent(initialIndentLevel, formatParams)
});
}
return result;
}
return [];
},
getFoldingRanges(document: TextDocument): FoldingRange[] {
updateCurrentTextDocument(document);
let spans = jsLanguageService.getOutliningSpans(FILE_NAME);
let ranges: FoldingRange[] = [];
for (let span of spans) {
let curr = convertRange(currentTextDocument, span.textSpan);
let startLine = curr.start.line;
let endLine = curr.end.line;
if (startLine < endLine) {
let foldingRange: FoldingRange = { startLine, endLine };
let match = document.getText(curr).match(/^\s*\/(?:(\/\s*#(?:end)?region\b)|(\*|\/))/);
if (match) {
foldingRange.kind = match[1] ? FoldingRangeKind.Region : FoldingRangeKind.Comment;
}
ranges.push(foldingRange);
}
}
return ranges;
},
onDocumentRemoved(document: TextDocument) {
jsDocuments.onDocumentRemoved(document);
},
dispose() {
jsLanguageService.dispose();
jsDocuments.dispose();
}
};
}