|
1 | 1 | 'use babel';
|
2 | 2 |
|
3 |
| -export default { |
4 |
| - config: { |
5 |
| - // It should be noted that I, Kepler, hate these Config names. However these |
6 |
| - // are the names in use by many people. Changing them for the sake of clean |
7 |
| - // of clean code would cause a mess for our users. Because of this we |
8 |
| - // override the titles the editor gives them in the settings pane. |
9 |
| - execPath: { |
10 |
| - type: 'string', |
11 |
| - default: 'clang', |
12 |
| - }, |
13 |
| - clangIncludePaths: { |
14 |
| - type: 'array', |
15 |
| - default: ['.'], |
16 |
| - }, |
17 |
| - clangSuppressWarnings: { |
18 |
| - type: 'boolean', |
19 |
| - default: false, |
20 |
| - }, |
21 |
| - clangDefaultCFlags: { |
22 |
| - type: 'string', |
23 |
| - default: '-Wall', |
24 |
| - }, |
25 |
| - clangDefaultCppFlags: { |
26 |
| - type: 'string', |
27 |
| - default: '-Wall -std=c++11', |
28 |
| - }, |
29 |
| - clangDefaultObjCFlags: { |
30 |
| - type: 'string', |
31 |
| - default: '', |
32 |
| - }, |
33 |
| - clangDefaultObjCppFlags: { |
34 |
| - type: 'string', |
35 |
| - default: '', |
36 |
| - }, |
37 |
| - clangErrorLimit: { |
38 |
| - type: 'integer', |
39 |
| - default: 0, |
40 |
| - }, |
41 |
| - verboseDebug: { |
42 |
| - type: 'boolean', |
43 |
| - default: false, |
44 |
| - }, |
45 |
| - }, |
| 3 | +// eslint-disable-next-line import/extensions, import/no-extraneous-dependencies |
| 4 | +import { CompositeDisposable } from 'atom'; |
| 5 | +import { dirname, extname } from 'path'; |
46 | 6 |
|
| 7 | +let helpers = null; |
| 8 | +let clangFlags = null; |
| 9 | +const regex = /(.+):(\d+):(\d+):(?:{(\d+):(\d+)-(\d+):(\d+)}.*:)? ([\w \\-]+): (.*)/g; |
| 10 | + |
| 11 | +export default { |
47 | 12 | activate() {
|
48 | 13 | require('atom-package-deps').install('linter-clang');
|
| 14 | + |
| 15 | + // FIXME: Remove backwards compatibility in a future minor version |
| 16 | + const oldPath = atom.config.get('linter-clang.execPath'); |
| 17 | + if (oldPath !== undefined) { |
| 18 | + atom.config.unset('linter-clang.execPath'); |
| 19 | + if (oldPath !== 'clang') { |
| 20 | + // If the old config wasn't set to the default migrate it over |
| 21 | + atom.config.set('linter-pylint.executablePath', oldPath); |
| 22 | + } |
| 23 | + } |
| 24 | + |
| 25 | + this.subscriptions = new CompositeDisposable(); |
| 26 | + this.subscriptions.add( |
| 27 | + atom.config.observe('linter-clang.executablePath', (value) => { |
| 28 | + this.executablePath = value; |
| 29 | + }), |
| 30 | + ); |
| 31 | + this.subscriptions.add( |
| 32 | + atom.config.observe('linter-clang.clangIncludePaths', (value) => { |
| 33 | + this.clangIncludePaths = value; |
| 34 | + }), |
| 35 | + ); |
| 36 | + this.subscriptions.add( |
| 37 | + atom.config.observe('linter-clang.clangSuppressWarnings', (value) => { |
| 38 | + this.clangSuppressWarnings = value; |
| 39 | + }), |
| 40 | + ); |
| 41 | + this.subscriptions.add( |
| 42 | + atom.config.observe('linter-clang.clangDefaultCFlags', (value) => { |
| 43 | + this.clangDefaultCFlags = value; |
| 44 | + }), |
| 45 | + ); |
| 46 | + this.subscriptions.add( |
| 47 | + atom.config.observe('linter-clang.clangDefaultCppFlags', (value) => { |
| 48 | + this.clangDefaultCppFlags = value; |
| 49 | + }), |
| 50 | + ); |
| 51 | + this.subscriptions.add( |
| 52 | + atom.config.observe('linter-clang.clangDefaultObjCFlags', (value) => { |
| 53 | + this.clangDefaultObjCFlags = value; |
| 54 | + }), |
| 55 | + ); |
| 56 | + this.subscriptions.add( |
| 57 | + atom.config.observe('linter-clang.clangDefaultObjCppFlags', (value) => { |
| 58 | + this.clangDefaultObjCppFlags = value; |
| 59 | + }), |
| 60 | + ); |
| 61 | + this.subscriptions.add( |
| 62 | + atom.config.observe('linter-clang.clangErrorLimit', (value) => { |
| 63 | + this.clangErrorLimit = value; |
| 64 | + }), |
| 65 | + ); |
| 66 | + }, |
| 67 | + |
| 68 | + deactivate() { |
| 69 | + this.subscriptions.dispose(); |
49 | 70 | },
|
50 | 71 |
|
51 | 72 | provideLinter() {
|
52 |
| - const helpers = require('atom-linter'); |
53 |
| - const clangFlags = require('clang-flags'); |
54 |
| - const regex = '(?<file>.+):(?<line>\\d+):(?<col>\\d+):({(?<lineStart>\\d+):(?<colStart>\\d+)-(?<lineEnd>\\d+):(?<colEnd>\\d+)}.*:)? (?<type>[\\w \\-]+): (?<message>.*)'; |
55 | 73 | return {
|
56 | 74 | name: 'clang',
|
57 |
| - grammarScopes: ['source.c', 'source.cpp', 'source.objc', 'source.objcpp'], |
58 | 75 | scope: 'file',
|
59 |
| - lintOnFly: false, |
60 |
| - lint: (activeEditor) => { |
61 |
| - const command = atom.config.get('linter-clang.execPath'); |
62 |
| - const file = activeEditor.getPath(); |
63 |
| - const args = ['-fsyntax-only', |
| 76 | + lintsOnChange: false, |
| 77 | + grammarScopes: ['source.c', 'source.cpp', 'source.objc', 'source.objcpp'], |
| 78 | + lint: async (editor) => { |
| 79 | + if (helpers === null) { |
| 80 | + helpers = require('atom-linter'); |
| 81 | + } |
| 82 | + if (clangFlags === null) { |
| 83 | + clangFlags = require('clang-flags'); |
| 84 | + } |
| 85 | + |
| 86 | + const filePath = editor.getPath(); |
| 87 | + const fileExt = extname(filePath); |
| 88 | + const fileText = editor.getText(); |
| 89 | + |
| 90 | + const args = [ |
| 91 | + '-fsyntax-only', |
64 | 92 | '-fno-caret-diagnostics',
|
65 | 93 | '-fno-diagnostics-fixit-info',
|
66 | 94 | '-fdiagnostics-print-source-range-info',
|
67 |
| - '-fexceptions']; |
| 95 | + '-fexceptions', |
| 96 | + `-ferror-limit=${this.clangErrorLimit}`, |
| 97 | + ]; |
68 | 98 |
|
69 |
| - const grammar = activeEditor.getGrammar().name; |
| 99 | + const grammar = editor.getGrammar().name; |
70 | 100 |
|
71 |
| - if (/^C\+\+/.test(grammar)) { |
72 |
| - // const language = "c++"; |
73 |
| - args.push('-xc++'); |
74 |
| - args.push(...atom.config.get('linter-clang.clangDefaultCppFlags').split(/\s+/)); |
75 |
| - } |
76 |
| - if (grammar === 'Objective-C++') { |
77 |
| - // const language = "objective-c++"; |
78 |
| - args.push('-xobjective-c++'); |
79 |
| - args.push(...atom.config.get('linter-clang.clangDefaultObjCppFlags').split(/\s+/)); |
| 101 | + switch (grammar) { |
| 102 | + case 'Objective-C': |
| 103 | + args.push('-xobjective-c'); |
| 104 | + args.push(...this.clangDefaultObjCFlags.split(/\s+/)); |
| 105 | + break; |
| 106 | + case 'Objective-C++': |
| 107 | + args.push('-xobjective-c++'); |
| 108 | + args.push(...this.clangDefaultObjCppFlags.split(/\s+/)); |
| 109 | + break; |
| 110 | + case 'C': |
| 111 | + args.push('-xc'); |
| 112 | + args.push(...this.clangDefaultCFlags.split(/\s+/)); |
| 113 | + break; |
| 114 | + default: |
| 115 | + case 'C++': |
| 116 | + case 'C++14': |
| 117 | + args.push('-xc++'); |
| 118 | + args.push(...this.clangDefaultCppFlags.split(/\s+/)); |
| 119 | + break; |
80 | 120 | }
|
81 |
| - if (grammar === 'C') { |
82 |
| - // const language = "c"; |
83 |
| - args.push('-xc'); |
84 |
| - args.push(...atom.config.get('linter-clang.clangDefaultCFlags').split(/\s+/)); |
85 |
| - } |
86 |
| - if (grammar === 'Objective-C') { |
87 |
| - // const language = "objective-c"; |
88 |
| - args.push('-xobjective-c'); |
89 |
| - args.push(...atom.config.get('linter-clang.clangDefaultObjCFlags').split(/\s+/)); |
| 121 | + |
| 122 | + if (fileExt === '.hpp' || fileExt === '.hh' || fileExt === '.h') { |
| 123 | + // Don't warn about #pragma once when linting header files |
| 124 | + args.push('-Wno-pragma-once-outside-header'); |
90 | 125 | }
|
91 | 126 |
|
92 |
| - args.push(`-ferror-limit=${atom.config.get('linter-clang.clangErrorLimit')}`); |
93 |
| - if (atom.config.get('linter-clang.clangSuppressWarnings')) { |
| 127 | + if (this.clangSuppressWarnings) { |
94 | 128 | args.push('-w');
|
95 | 129 | }
|
96 |
| - if (atom.config.get('linter-clang.verboseDebug')) { |
| 130 | + |
| 131 | + if (atom.inDevMode()) { |
97 | 132 | args.push('--verbose');
|
98 | 133 | }
|
99 | 134 |
|
100 |
| - atom.config.get('linter-clang.clangIncludePaths').forEach(path => |
| 135 | + this.clangIncludePaths.forEach(path => |
101 | 136 | args.push(`-I${path}`),
|
102 | 137 | );
|
103 | 138 |
|
104 | 139 | try {
|
105 |
| - const flags = clangFlags.getClangFlags(activeEditor.getPath()); |
| 140 | + const flags = clangFlags.getClangFlags(filePath); |
106 | 141 | if (flags) {
|
107 | 142 | args.push(...flags);
|
108 | 143 | }
|
109 | 144 | } catch (error) {
|
110 |
| - if (atom.config.get('linter-clang.verboseDebug')) { |
| 145 | + if (atom.inDevMode()) { |
111 | 146 | // eslint-disable-next-line no-console
|
112 | 147 | console.log(error);
|
113 | 148 | }
|
114 | 149 | }
|
115 | 150 |
|
116 |
| - // The file is added to the arguments last. |
117 |
| - args.push(file); |
| 151 | + args.push(filePath); |
| 152 | + |
| 153 | + let [projectPath] = atom.project.relativizePath(filePath); |
| 154 | + if (projectPath === null) { |
| 155 | + projectPath = dirname(filePath); |
| 156 | + } |
| 157 | + |
118 | 158 | const execOpts = {
|
119 | 159 | stream: 'stderr',
|
120 | 160 | allowEmptyStderr: true,
|
| 161 | + cwd: projectPath, |
121 | 162 | };
|
122 |
| - return helpers.exec(command, args, execOpts).then(output => |
123 |
| - helpers.parse(output, regex), |
124 |
| - ); |
| 163 | + |
| 164 | + const output = await helpers.exec(this.executablePath, args, execOpts); |
| 165 | + |
| 166 | + if (editor.getText() !== fileText) { |
| 167 | + // Editor contents have changed, tell Linter not to update results |
| 168 | + // eslint-disable-next-line no-console |
| 169 | + console.warn('linter-clang: Editor contents changed, not updating results'); |
| 170 | + return null; |
| 171 | + } |
| 172 | + |
| 173 | + const toReturn = []; |
| 174 | + let match = regex.exec(output); |
| 175 | + |
| 176 | + while (match !== null) { |
| 177 | + let position; |
| 178 | + if (match[4] !== undefined) { |
| 179 | + const lineStart = Number.parseInt(match[4], 10) - 1; |
| 180 | + const colStart = Number.parseInt(match[5], 10) - 1; |
| 181 | + const lineEnd = Number.parseInt(match[6], 10) - 1; |
| 182 | + const colEnd = Number.parseInt(match[7], 10) - 1; |
| 183 | + position = [[lineStart, colStart], [lineEnd, colEnd]]; |
| 184 | + } else { |
| 185 | + const line = Number.parseInt(match[2], 10) - 1; |
| 186 | + const col = Number.parseInt(match[3], 10) - 1; |
| 187 | + position = helpers.generateRange(editor, line, col); |
| 188 | + } |
| 189 | + const severity = /error/.test(match[8]) ? 'error' : 'warning'; |
| 190 | + toReturn.push({ |
| 191 | + severity, |
| 192 | + location: { file: match[1], position }, |
| 193 | + excerpt: match[9], |
| 194 | + }); |
| 195 | + match = regex.exec(output); |
| 196 | + } |
| 197 | + |
| 198 | + return toReturn; |
125 | 199 | },
|
126 | 200 | };
|
127 | 201 | },
|
|
0 commit comments