UNPKG

els-addon-typed-templates

Version:
266 lines (237 loc) 9.43 kB
import { URI } from "vscode-uri"; import * as ts from "typescript"; import { Location, Range, DiagnosticSeverity, Diagnostic } from "vscode-languageserver"; import * as fs from "fs"; import * as path from "path"; import { itemKind } from './utils'; import { serializeArgumentName } from './ast-helpers'; export function normalizeDefinitions(results, root: string) { return (results || []) .map(el => { return tsDefinitionToLocation(el, root); }).filter((el) => el !== null); } const ignoreNames = ['willDestroy', 'toString']; export function normalizeCompletions(tsResults, realPath, isArg) { return (tsResults ? tsResults.entries : []) .filter(({ name }) => !ignoreNames.includes(name) && !name.startsWith("_t") && !name.includes(' - ') && name !== 'globalScope' && name !== 'defaultYield') .map(el => { return { label: isArg ? serializeArgumentName(realPath) + el.name : realPath + el.name, data: el.name, kind: itemKind(el.kind) }; }); // .map(el => { // let fixedLabelParts = el.label.split('.'); // fixedLabelParts[fixedLabelParts.length - 1] = el.data; // return { // kind: el.kind, // label: fixedLabelParts.join('.') // } // }); } export function offsetToRange(start, limit, source) { let rLines = /(.*?(?:\r\n?|\n|$))/gm; let startLine = source.slice(0, start).match(rLines) || []; if (!source || startLine.length < 2) { return Range.create(0, 0, 0, 0); } let line = startLine.length - 2; let col = startLine[startLine.length - 2].length; let endLine = source.slice(start, limit).match(rLines) || []; let endCol = col; let endLineNumber = line; if (endLine.length === 1) { endCol = col + limit; endLineNumber = line + endLine.length - 1; } else { endCol = endLine[endLine.length - 1].length; } return Range.create(line, col, endLineNumber, endCol); } export function tsDefinitionToLocation(el, root) { let scope = el.textSpan; let fullPath = path.resolve(el.fileName); if (!fs.existsSync(fullPath)) { fullPath = path.resolve(path.join(root, el.fileName)); if (!fs.existsSync(fullPath)) { return null; } } let file = fs.readFileSync(fullPath, "utf8"); return Location.create( URI.file(fullPath).toString(), offsetToRange(scope.start, scope.length, file) ); } function messageConverter(msg) { if (msg.startsWith("The 'this' context of type 'this' is not assignable to method's 'this' of type 'null'.")) { return "Unable to find context, is file created and property defined?" } console.log(msg); return msg; } function getSeverity(msg: string): DiagnosticSeverity { let severity: DiagnosticSeverity = DiagnosticSeverity.Error; if (msg.startsWith("Object is possibly 'undefined'")) { severity = DiagnosticSeverity.Warning; } else if (msg.startsWith("Object is possibly 'null'")) { severity = DiagnosticSeverity.Warning; } return severity; } function toFullDiagnostic(err: ts.Diagnostic) { if (!err.file || err.start === undefined) { return null; } let preErrorText = err.file.text.slice(0, err.start); let postErrorText = err.file.text.slice(err.start, err.file.text.length); // try { // console.log('err.file.fileName', err.file.fileName); // console.log('start', err.start); // console.log('err.slice', err.file.text.slice(err.start, 100)); // console.log('err.code', err.code); // console.log('err.category', err.category); // console.log('err.related', err.relatedInformation); // console.log('err.source', err.source); // console.log('err.msg', err.messageText); // } catch(e) { // console.log('err:', e); // } if (err.start < err.file.text.indexOf('@mark-meaningful-issues-start')) { return null; } let closestLeftMark = postErrorText.indexOf('["'); let closestRightMarkOffset = postErrorText.indexOf('"]'); let maybeMark = err.file.text.slice(closestLeftMark + err.start, closestRightMarkOffset + err.start); let hasNewline = err.file.text.slice(err.start, err.start + closestLeftMark).split('\n').length > 1; maybeMark = maybeMark.slice(maybeMark.indexOf('[') + 2, maybeMark.indexOf(']')).trim().split(' - ')[0]; let start, end; if (maybeMark.includes(':') && !hasNewline) { [start, end] = maybeMark.split(':'); } else { let preError = preErrorText.slice(preErrorText.lastIndexOf('//@mark'), preErrorText.length); let mark = preError.slice(preError.indexOf('[') + 1, preError.indexOf(']')).trim(); [start, end] = mark.split(':'); } if (! start || ! end) { let postError = err.file.text.slice(err.start, err.file.text.length); let postErrorMark = postError.slice(postError.indexOf('/*@path-mark ') + 13,postError.indexOf('*/')); [start, end] = postErrorMark.split(':'); if (!start || ! end) { console.log(err); return null; } } // console.log({mark, start, end}) // console.log('preErrorText',preErrorText.slice(preErrorText.lastIndexOf('//@mark ') + 8, preErrorText.lastIndexOf('//@mark ') + 40)); let [startCol, startRow] = start.split(',').map((e)=>parseInt(e, 10)); let [endCol, endRow] = end.split(',').map((e)=>parseInt(e, 10)); let msgText = diagnosticToString(err.messageText); /* since ember components in addons may be like ... export default Ember.Component.extend(Base, PromiseResolver, { it's really tricky to get typings for it at all, and I prefer to skip warnings for it in next lines */ if (msgText.startsWith("Object is of type 'unknown'")) { return null; } if (msgText.startsWith("Type 'any' is not assignable to type 'never'")) { return null; } if (msgText.startsWith("Property 'args' does not exist on type")) { return null; } if (msgText.startsWith("Expected 0 arguments, but got 2.")) { return null; } if (msgText.startsWith("Cannot invoke an object which is possibly")) { return null; } return { severity: getSeverity(msgText), range: Range.create(startCol - 1, startRow, endCol - 1, endRow), message: messageConverter(msgText), source: "typed-templates" }; } // regards to https://github.com/dfreeman/ember-typed-templates-vscode/blob/master/src/server/server.ts#L172 function diagnosticToString(message: string | ts.DiagnosticMessageChain, indent = ''): string { if (typeof message === 'string') { return `${indent}${message}`; } else if (message.next && message.next.length) { let items = message.next.map((msg)=>diagnosticToString(msg, `${indent} `)) return `${indent}${message.messageText}\n${items.join('\n')}`; } else { return `${indent}${message.messageText}`; } } export function getFullSemanticDiagnostics(service: ts.LanguageService, fileName) { const tsDiagnostics = service.getSemanticDiagnostics(fileName); const results = tsDiagnostics.map((error: any) => toFullDiagnostic(error) ).filter((el)=>el !== null); const diagnostics: Diagnostic[] = results as Diagnostic[]; return diagnostics; } export function getSemanticDiagnostics(server, service, templateRange, fileName , focusPath, uri) { // console.log(service.getSyntacticDiagnostics(fileName).map((el)=>{ // console.log('getSyntacticDiagnostics', el.messageText, el.start, el.length); // })); // console.log('getSemanticDiagnostics', fileName); const tsDiagnostics = service.getSemanticDiagnostics(fileName); const diagnostics: Diagnostic[] = tsDiagnostics.map((error: any) => toDiagnostic(error, templateRange, focusPath) ); server.connection.sendDiagnostics({ uri, diagnostics }); // console.log(service.getSemanticDiagnostics(fileName).map((el)=>{ // const diagnostics: Diagnostic[] = errors.map((error: any) => toDiagnostic(el)); // server.connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); // console.log('getSemanticDiagnostics', el.messageText, el.start, el.length); // })); // console.log(service.getSuggestionDiagnostics(fileName).map((el)=>{ // console.log('getSuggestionDiagnostics', el.messageText, el.start, el.length); // })); // console.log('getCompilerOptionsDiagnostics', service.getCompilerOptionsDiagnostics()); } export function toDiagnostic( err, [startIndex, endIndex], focusPath ): Diagnostic { let errText = err.file.text.slice(err.start, err.start + err.length); if ( (err.start >= startIndex && err.length + err.start <= endIndex) || errText.startsWith("return ") ) { let loc = focusPath.node.loc; return { severity: getSeverity(err.messageText), range: loc ? Range.create( loc.start.line - 1, loc.start.column, loc.end.line - 1, loc.end.column ) : Range.create(0, 0, 0, 0), message: messageConverter(err.messageText), source: "typed-templates" }; } else { return { severity: getSeverity(err.messageText), range: offsetToRange(0, 0, ""), message: messageConverter(err.messageText), source: "typed-templates" }; } }