Skip to content
This repository was archived by the owner on Aug 7, 2023. It is now read-only.

Commit d7a61bc

Browse files
author
Суворов Станислав
committed
Fixed code and tests
1 parent 1c23506 commit d7a61bc

File tree

5 files changed

+132
-88
lines changed

5 files changed

+132
-88
lines changed

lib/cached_result.coffee

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class CachedResult
2+
currentTTL: null
3+
result: null
4+
5+
constructor: (result, TTL = 5) ->
6+
@currentTTL = TTL
7+
@result = result
8+
9+
getResult: =>
10+
if do @valid
11+
@currentTTL -= 1
12+
@result
13+
else
14+
null
15+
16+
valid: =>
17+
@currentTTL > 0
18+
19+
module.exports = CachedResult

lib/linter-rust.coffee

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ fs = require 'fs'
22
path = require 'path'
33
XRegExp = require 'xregexp'
44
semver = require 'semver'
5-
sb_exec = require 'sb-exec'
65
{CompositeDisposable} = require 'atom'
6+
atom_linter = require 'atom-linter'
77
errorModes = require './mode'
8+
CachedResult = require './cached_result'
89

910
class LinterRust
1011
patternRustcVersion: XRegExp('rustc (?<version>1.\\d+.\\d+)(?:(?:-(?<nightly>nightly)|(?:[^\\s]+))? \
@@ -71,7 +72,7 @@ class LinterRust
7172
additional = if env.RUSTFLAGS? then ' ' + env.RUSTFLAGS else ''
7273
env.RUSTFLAGS = '--error-format=json' + additional
7374

74-
sb_exec.exec(command, args, {env: env, cwd: cwd, stream: 'both'})
75+
atom_linter.exec(command, args, {env: env, cwd: cwd, stream: 'both'})
7576
.then (result) =>
7677
{stdout, stderr, exitCode} = result
7778
# first, check if an output says specified features are invalid
@@ -94,7 +95,7 @@ class LinterRust
9495

9596
# call a needed parser
9697
output = errorMode.neededOutput(stdout, stderr)
97-
messages = errorMode.parse output, @disabledWarnings
98+
messages = errorMode.parse output, {@disabledWarnings, textEditor}
9899

99100
# correct file paths
100101
messages.forEach (message) ->
@@ -120,7 +121,7 @@ class LinterRust
120121

121122
initCmd: (editingFile) =>
122123
curDir = path.dirname editingFile
123-
cargoManifestPath = @locateCargo path.dirname editingFile
124+
cargoManifestPath = @locateCargo curDir
124125
if not @useCargo or not cargoManifestPath
125126
@decideErrorMode(curDir, 'rustc').then (mode) =>
126127
mode.buildArguments(this, [editingFile, cargoManifestPath]).then (cmd) =>
@@ -140,32 +141,43 @@ class LinterRust
140141
result.push ['--cfg', "feature=\"#{f}\""]
141142
result
142143

144+
cachedErrorMode: null
145+
143146
decideErrorMode: (curDir, commandMode) =>
144-
# current dir is set to handle overrides
145-
sb_exec.exec(@rustcPath, ['--version'], {stream: 'stdout', cwd: curDir, stdio: 'pipe'}).then (stdout) =>
146-
try
147-
match = XRegExp.exec(stdout, @patternRustcVersion)
148-
if match
149-
nightlyWithJSON = match.nightly and match.date > '2016-08-08'
150-
stableWithJSON = not match.nightly and semver.gte(match.version, '1.12.0')
151-
canUseIntermediateJSON = nightlyWithJSON or stableWithJSON
152-
switch commandMode
153-
when 'cargo'
154-
canUseProperCargoJSON = match.nightly and match.date > '2016-10-10'
155-
if canUseProperCargoJSON
156-
errorModes.JSON_CARGO
157-
# this mode is used only through August till October, 2016
158-
else if canUseIntermediateJSON
159-
errorModes.FLAGS_JSON_CARGO
160-
else
161-
errorModes.OLD_CARGO
162-
when 'rustc'
163-
if canUseIntermediateJSON
164-
errorModes.JSON_RUSTC
165-
else
166-
errorModes.OLD_RUSTC
167-
else
168-
throw 'rustc returned inapplicable result: ' + stdout
147+
# error mode is cached to avoid delays
148+
if @cachedErrorMode? and do @cachedErrorMode.valid
149+
Promise.resolve().then () =>
150+
do @cachedErrorMode.getResult
151+
else
152+
# current dir is set to handle overrides
153+
atom_linter.exec(@rustcPath, ['--version'], {cwd: curDir}).then (stdout) =>
154+
try
155+
match = XRegExp.exec(stdout, @patternRustcVersion)
156+
if match
157+
nightlyWithJSON = match.nightly and match.date > '2016-08-08'
158+
stableWithJSON = not match.nightly and semver.gte(match.version, '1.12.0')
159+
canUseIntermediateJSON = nightlyWithJSON or stableWithJSON
160+
switch commandMode
161+
when 'cargo'
162+
canUseProperCargoJSON = match.nightly and match.date > '2016-10-10'
163+
if canUseProperCargoJSON
164+
errorModes.JSON_CARGO
165+
# this mode is used only through August till October, 2016
166+
else if canUseIntermediateJSON
167+
errorModes.FLAGS_JSON_CARGO
168+
else
169+
errorModes.OLD_CARGO
170+
when 'rustc'
171+
if canUseIntermediateJSON
172+
errorModes.JSON_RUSTC
173+
else
174+
errorModes.OLD_RUSTC
175+
else
176+
throw 'rustc returned unexpected result: ' + stdout
177+
.then (result) =>
178+
@cachedErrorMode = new CachedResult(result)
179+
result
180+
169181

170182
locateCargo: (curDir) =>
171183
root_dir = if /^win/.test process.platform then /^.:\\$/ else /^\/$/

lib/mode.coffee

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
XRegExp = require 'xregexp'
2-
sb_exec = require 'sb-exec'
32
path = require 'path'
3+
atom_linter = require 'atom-linter'
4+
CachedResult = require './cached_result'
45

56
pattern = XRegExp('(?<file>[^\n\r]+):(?<from_line>\\d+):(?<from_col>\\d+):\\s*\
67
(?<to_line>\\d+):(?<to_col>\\d+)\\s+\
78
((?<error>error|fatal error)|(?<warning>warning)|(?<info>note|help)):\\s+\
89
(?<message>.+?)[\n\r]+($|(?=[^\n\r]+:\\d+))', 's')
910

10-
parseOldMessages = (output, disabledWarnings) ->
11+
parseOldMessages = (output, {disabledWarnings, textEditor}) ->
1112
elements = []
1213
XRegExp.forEach output, pattern, (match) ->
13-
if match.from_col == match.to_col
14-
match.to_col = parseInt(match.to_col) + 1
15-
range = [
16-
[match.from_line - 1, match.from_col - 1],
17-
[match.to_line - 1, match.to_col - 1]
18-
]
14+
range = if match.from_col == match.to_col and match.from_line == match.to_line
15+
atom_linter.rangeFromLineNumber(textEditor, Number.parseInt(match.from_line, 10) - 1, Number.parseInt(match.from_col, 10) - 1)
16+
else
17+
[
18+
[match.from_line - 1, match.from_col - 1],
19+
[match.to_line - 1, match.to_col - 1]
20+
]
1921
level = if match.error then 'error'
2022
else if match.warning then 'warning'
2123
else if match.info then 'info'
@@ -29,7 +31,7 @@ parseOldMessages = (output, disabledWarnings) ->
2931
elements.push element
3032
buildMessages elements, disabledWarnings
3133

32-
parseJsonMessages = (messages, disabledWarnings) ->
34+
parseJsonMessages = (messages, {disabledWarnings}) ->
3335
elements = []
3436
for input in messages
3537
continue unless input and input.spans
@@ -39,7 +41,7 @@ parseJsonMessages = (messages, disabledWarnings) ->
3941
[primary_span.line_start - 1, primary_span.column_start - 1],
4042
[primary_span.line_end - 1, primary_span.column_end - 1]
4143
]
42-
input.level = 'error' if input == 'fatal error'
44+
input.level = 'error' if input.level == 'fatal error'
4345
element =
4446
type: input.level
4547
message: input.message
@@ -57,13 +59,17 @@ parseJsonMessages = (messages, disabledWarnings) ->
5759
elements.push element
5860
buildMessages elements, disabledWarnings
5961

60-
parseJsonOutput = (output, disabledWarnings) ->
62+
parseJsonOutput = (output, {disabledWarnings, additionalFilter} ) ->
6163
results = output.split('\n').map (message) ->
6264
message = message.trim()
6365
if message.startsWith '{'
64-
JSON.parse message
66+
json = JSON.parse message
67+
if additionalFilter?
68+
additionalFilter(json)
69+
else
70+
json
6571
.filter (m) -> m?
66-
parseJsonMessages results, disabledWarnings
72+
parseJsonMessages results, {disabledWarnings}
6773

6874
buildMessages = (elements, disabledWarnings) ->
6975
messages = []
@@ -134,7 +140,36 @@ buildRustcArguments = (linter, paths) ->
134140
cmd = cmd.concat [editingFile]
135141
[editingFile, cmd]
136142

143+
cachedUsingMultitoolForClippy = null
144+
137145
buildCargoArguments = (linter, cargoManifestPath) ->
146+
buildCargoPath = (cargoPath, cargoCommand) ->
147+
# the result is cached to avoid delays
148+
if cachedUsingMultitoolForClippy? and do cachedUsingMultitoolForClippy.valid
149+
Promise.resolve().then () =>
150+
do cachedUsingMultitoolForClippy.getResult
151+
else
152+
# Decide if should use older multirust or newer rustup
153+
usingMultitoolForClippy =
154+
atom_linter.exec 'rustup', ['--version'], {ignoreExitCode: true}
155+
.then ->
156+
result: true, tool: 'rustup'
157+
.catch ->
158+
# Try to use older multirust at least
159+
atom_linter.exec 'multirust', ['--version'], {ignoreExitCode: true}
160+
.then ->
161+
result: true, tool: 'multirust'
162+
.catch ->
163+
result: false
164+
usingMultitoolForClippy.then (canUseMultirust) ->
165+
if cargoCommand == 'clippy' and canUseMultirust.result
166+
[canUseMultirust.tool, 'run', 'nightly', 'cargo']
167+
else
168+
[cargoPath]
169+
.then (cached) =>
170+
cachedUsingMultitoolForClippy = new CachedResult(cached, 20)
171+
cached
172+
138173
cargoArgs = switch linter.cargoCommand
139174
when 'check' then ['check']
140175
when 'test' then ['test', '--no-run']
@@ -143,42 +178,22 @@ buildCargoArguments = (linter, cargoManifestPath) ->
143178
else ['build']
144179

145180
compilationFeatures = linter.compilationFeatures(true)
146-
buildCargoPath(linter.cargoPath).then (cmd) ->
181+
buildCargoPath(linter.cargoPath, linter.cargoCommand).then (cmd) ->
147182
cmd = cmd
148183
.concat cargoArgs
149184
.concat ['-j', linter.jobsNumber]
150185
cmd = cmd.concat compilationFeatures if compilationFeatures
151186
cmd = cmd.concat ['--manifest-path', cargoManifestPath]
152187
[cargoManifestPath, cmd]
153188

154-
buildCargoPath = (cargoPath, cargoCommand) ->
155-
usingMultitoolForClippy().then (canUseMultirust) ->
156-
if cargoCommand == 'clippy' and canUseMultirust.result
157-
[canUseMultirust.tool, 'run', 'nightly', 'cargo']
158-
else
159-
[cargoPath]
160-
161-
usingMultitoolForClippy = () ->
162-
# Try to use rustup
163-
sb_exec.exec 'rustup', ['--version'], {ignoreExitCode: true}
164-
.then ->
165-
result: true, tool: 'rustup'
166-
.catch ->
167-
# Try to use odler multirust at least
168-
sb_exec.exec 'multirust', ['--version'], {ignoreExitCode: true}
169-
.then ->
170-
result: true, tool: 'multirust'
171-
.catch ->
172-
result: false
173-
174189
# These define the behabiour of each error mode linter-rust has
175190
errorModes =
176191
JSON_RUSTC:
177192
neededOutput: (stdout, stderr) ->
178193
stderr
179194

180-
parse: (output, disabledWarnings) =>
181-
parseJsonOutput output, disabledWarnings
195+
parse: (output, options) =>
196+
parseJsonOutput output, options
182197

183198
buildArguments: (linter, file) ->
184199
buildRustcArguments(linter, file).then (cmd_res) ->
@@ -190,16 +205,11 @@ errorModes =
190205
neededOutput: (stdout, stderr) ->
191206
stdout
192207

193-
parse: (output, disabledWarnings) ->
194-
checkCompilerMessage = (message) ->
195-
message = message.trim()
196-
if message.startsWith '{'
197-
input = JSON.parse message
198-
input.message if input? and input.reason == "compiler-message"
199-
200-
results = output.split '\n'
201-
results = results.map(checkCompilerMessage).filter((i) -> i?)
202-
parseJsonMessages results, disabledWarnings
208+
parse: (output, options) ->
209+
options.additionalFilter = (json) ->
210+
if input? and input.reason == "compiler-message"
211+
input.message
212+
parseJsonOutput output, options
203213

204214
buildArguments: (linter, file) ->
205215
buildCargoArguments(linter, file).then (cmd_res) ->

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
}
1717
},
1818
"dependencies": {
19+
"atom-linter": "^8.0.0",
1920
"atom-package-deps": "^4.3.0",
2021
"semver": "^5.3.0",
21-
"sb-exec": "^3.1.0",
2222
"xregexp": "~3.1.0"
2323
},
2424
"package-deps": [

spec/parse-spec.coffee

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ linter = new LinterRust()
55

66
describe "errorModes::OLD_RUSTC::parse", ->
77
it "should return 0 messages for an empty string", ->
8-
expect(errorModes.OLD_RUSTC.parse('', [])).toEqual([])
8+
expect(errorModes.OLD_RUSTC.parse('', {})).toEqual([])
99

1010
it "should properly parse one line error message", ->
11-
expect(errorModes.OLD_RUSTC.parse('my/awesome file.rs:1:2: 3:4 error: my awesome text\n', []))
11+
expect(errorModes.OLD_RUSTC.parse('my/awesome file.rs:1:2: 3:4 error: my awesome text\n', {}))
1212
.toEqual([{
1313
type: 'Error'
1414
text: 'my awesome text'
@@ -17,7 +17,7 @@ describe "errorModes::OLD_RUSTC::parse", ->
1717
}])
1818

1919
it "should properly parse one line warning message", ->
20-
expect(errorModes.OLD_RUSTC.parse('foo:33:44: 22:33 warning: äüö<>\n', []))
20+
expect(errorModes.OLD_RUSTC.parse('foo:33:44: 22:33 warning: äüö<>\n', {}))
2121
.toEqual([{
2222
type: 'Warning',
2323
text: 'äüö<>'
@@ -26,47 +26,50 @@ describe "errorModes::OLD_RUSTC::parse", ->
2626
}])
2727

2828
it "should return messages with a range of at least one character", ->
29-
expect(errorModes.OLD_RUSTC.parse('foo:1:1: 1:1 error: text\n', []))
29+
editor = atom.workspace.buildTextEditor()
30+
editor.setText 'fn main() {\nprintln!("Hi test");}\n'
31+
# expect(editor.getPath()).toContain 'c.coffee'
32+
expect(errorModes.OLD_RUSTC.parse('foo:1:1: 1:1 error: text\n', {textEditor: editor}))
3033
.toEqual([{
3134
type: 'Error'
3235
text: 'text'
3336
filePath: 'foo'
34-
range: [[0, 0], [0, 1]]
37+
range: [[0, 0], [0, 2]]
3538
}])
36-
expect(errorModes.OLD_RUSTC.parse('foo:1:1: 2:1 error: text\n', []))
39+
expect(errorModes.OLD_RUSTC.parse('foo:2:1: 2:1 error: text\n', {textEditor: editor}))
3740
.toEqual([{
3841
type: 'Error'
3942
text: 'text'
4043
filePath: 'foo'
41-
range: [[0, 0], [1, 1]]
44+
range: [[1, 0], [1, 7]]
4245
}])
4346

4447
it "should properly parse multiline messages", ->
4548
expect(errorModes.OLD_RUSTC.parse('bar:1:2: 3:4 error: line one\n\
46-
two\n', []))
49+
two\n', {}))
4750
.toEqual([
4851
{ type: 'Error', text: 'line one\ntwo', filePath: 'bar', range: [[0, 1], [2, 3]] }
4952
])
5053
expect(errorModes.OLD_RUSTC.parse('bar:1:2: 3:4 error: line one\n\
5154
two\n\
52-
foo:1:1: 1:2 warning: simple line\n', []))
55+
foo:1:1: 1:2 warning: simple line\n', {}))
5356
.toEqual([
5457
{ type: 'Error', text: 'line one\ntwo', filePath: 'bar', range: [[0, 1], [2, 3]] },
5558
{ type: 'Warning', text: 'simple line', filePath: 'foo', range: [[0, 0], [0, 1]] }
5659
])
5760
expect(errorModes.OLD_RUSTC.parse('bar:1:2: 3:4 error: line one\n\
5861
two\n\
5962
three\n\
60-
foo:1 shouldnt match', []))
63+
foo:1 shouldnt match', {}))
6164
.toEqual([
6265
{ type: 'Error', text: 'line one\ntwo\nthree', filePath: 'bar', range: [[0, 1], [2, 3]] }
6366
])
6467

6568
it "should also cope with windows line breaks", ->
66-
expect(errorModes.OLD_RUSTC.parse('a:1:2: 3:4 error: a\r\nb\n', [])[0].text)
69+
expect(errorModes.OLD_RUSTC.parse('a:1:2: 3:4 error: a\r\nb\n', {})[0].text)
6770
.toEqual('a\r\nb')
6871

69-
multi = errorModes.OLD_RUSTC.parse('a:1:2: 3:4 error: a\n\rb\n\rx:1:2: 3:4 error: asd\r\n', [])
72+
multi = errorModes.OLD_RUSTC.parse('a:1:2: 3:4 error: a\n\rb\n\rx:1:2: 3:4 error: asd\r\n', {})
7073
expect(multi[0].text).toEqual('a\n\rb')
7174
expect(multi[1].text).toEqual('asd')
7275

0 commit comments

Comments
 (0)