// ***************************************************************************** // Copyright (C) 2019 Ericsson and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { injectable, postConstruct } from '@theia/core/shared/inversify'; import { NamedProblemPattern, ProblemLocationKind, ProblemPattern, ProblemPatternContribution } from '../common'; @injectable() export class ProblemPatternRegistry { private readonly patterns = new Map(); private readyPromise = new Deferred(); @postConstruct() protected init(): void { this.fillDefaults(); this.readyPromise.resolve(); } onReady(): Promise { return this.readyPromise.promise; } /** * Add a problem pattern to the registry. * * @param definition the problem pattern to be added. */ register(value: ProblemPatternContribution | ProblemPatternContribution[]): Disposable { if (Array.isArray(value)) { const toDispose = new DisposableCollection(); value.forEach(problemPatternContribution => toDispose.push(this.register(problemPatternContribution))); return toDispose; } if (!value.name) { console.error('Only named Problem Patterns can be registered.'); return Disposable.NULL; } const problemPattern = ProblemPattern.fromProblemPatternContribution(value); return this.add(problemPattern.name!, problemPattern); } /** * Finds the problem pattern(s) from the registry with the given name. * * @param key the name of the problem patterns * @return a problem pattern or an array of the problem patterns associated with the name. If no problem patterns are found, `undefined` is returned. */ get(key: string): undefined | NamedProblemPattern | NamedProblemPattern[] { return this.patterns.get(key); } private add(key: string, value: ProblemPattern | ProblemPattern[]): Disposable { let toAdd: NamedProblemPattern | NamedProblemPattern[]; if (Array.isArray(value)) { toAdd = value.map(v => Object.assign(v, { name: key })); } else { toAdd = Object.assign(value, { name: key }); } this.patterns.set(key, toAdd); return Disposable.create(() => this.patterns.delete(key)); } // copied from https://github.com/Microsoft/vscode/blob/1.33.1/src/vs/workbench/contrib/tasks/common/problemMatcher.ts private fillDefaults(): void { this.add('msCompile', { regexp: /^(?:\s+\d+\>)?([^\s].*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\)\s*:\s+(error|warning|info)\s+(\w{1,2}\d+)\s*:\s*(.*)$/.source, kind: ProblemLocationKind.Location, file: 1, location: 2, severity: 3, code: 4, message: 5 }); this.add('gulp-tsc', { regexp: /^([^\s].*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\):\s+(\d+)\s+(.*)$/.source, kind: ProblemLocationKind.Location, file: 1, location: 2, code: 3, message: 4 }); this.add('cpp', { regexp: /^([^\s].*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\):\s+(error|warning|info)\s+(C\d+)\s*:\s*(.*)$/.source, kind: ProblemLocationKind.Location, file: 1, location: 2, severity: 3, code: 4, message: 5 }); this.add('csc', { regexp: /^([^\s].*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\):\s+(error|warning|info)\s+(CS\d+)\s*:\s*(.*)$/.source, kind: ProblemLocationKind.Location, file: 1, location: 2, severity: 3, code: 4, message: 5 }); this.add('vb', { regexp: /^([^\s].*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\):\s+(error|warning|info)\s+(BC\d+)\s*:\s*(.*)$/.source, kind: ProblemLocationKind.Location, file: 1, location: 2, severity: 3, code: 4, message: 5 }); this.add('lessCompile', { regexp: /^\s*(.*) in file (.*) line no. (\d+)$/.source, kind: ProblemLocationKind.Location, message: 1, file: 2, line: 3 }); this.add('jshint', { regexp: /^(.*):\s+line\s+(\d+),\s+col\s+(\d+),\s(.+?)(?:\s+\((\w)(\d+)\))?$/.source, kind: ProblemLocationKind.Location, file: 1, line: 2, character: 3, message: 4, severity: 5, code: 6 }); this.add('jshint-stylish', [ { regexp: /^(.+)$/.source, kind: ProblemLocationKind.Location, file: 1 }, { regexp: /^\s+line\s+(\d+)\s+col\s+(\d+)\s+(.+?)(?:\s+\((\w)(\d+)\))?$/.source, line: 1, character: 2, message: 3, severity: 4, code: 5, loop: true } ]); this.add('eslint-compact', { regexp: /^(.+):\sline\s(\d+),\scol\s(\d+),\s(Error|Warning|Info)\s-\s(.+)\s\((.+)\)$/.source, file: 1, kind: ProblemLocationKind.Location, line: 2, character: 3, severity: 4, message: 5, code: 6 }); this.add('eslint-stylish', [ { regexp: /^([^\s].*)$/.source, kind: ProblemLocationKind.Location, file: 1 }, { regexp: /^\s+(\d+):(\d+)\s+(error|warning|info)\s+(.+?)(?:\s\s+(.*))?$/.source, line: 1, character: 2, severity: 3, message: 4, code: 5, loop: true } ]); this.add('go', { regexp: /^([^:]*: )?((.:)?[^:]*):(\d+)(:(\d+))?: (.*)$/.source, kind: ProblemLocationKind.Location, file: 2, line: 4, character: 6, message: 7 }); } }