Closed ghost closed 7 years ago
@XenHat that is strange. I added your repository, but still WARNING: lslint deactivated, cannot locate 'lslint'
. I using LSL package and have lslint file in my bin folder, so I can get acces to it from terminal, from any place.
Can you show me the output from the console (or at least a few more lines)? I want to see where it tries to look.
THOUGHT: Perhaps your .bashrc
or .profile
adds a location into your path that isn't in the environment sublime-text is started with?
I know I changed some things related to path but I have done no testing from linux to ensure it detected it properly.
EDIT: Calling lslint
directly works in most cases within python, I'll set up a quick environment to test.
My bin folder added to path via .bashrc and /etc/environment
Hmmmm Curious. I will set up a quick Ubuntu and Dropbox (so handy) setup in a few minutes
I tried to run sublime-text exactly from this bin-folder, did not help.
Added symbolic link of lslint to /bin, still not working.
@Winterwolf fixed
@XenHat confirm! It works now. Thanks :+1:
Np, my bad ðŸ˜
@Sei-Lisa Concerning what doesn't work still, well... I'm not too sure how to properly describe the issue... So far, it doesn't work with scripts containing large amounts of preprocessor directives. I shall try with smaller ones.
The following script:
default
{
state_entry()
{
llOwnerSay("A thing!");
}
}
Produces the following output:
ORIGINAL_CODE:
0 |default
1 |{
2 | state_entry()
3 | {
4 | llOwnerSay("A thing!");
5 | }
6 |}
MCPP Output:
0 |#line 1 "<stdin>"
1 |default
2 |{
3 | state_entry()
4 | {
5 | llOwnerSay("A thing!");
6 | }
7 |}
8 |<stdin>:7: warning: End of input with no newline, supplemented newline
9 | }
DEBUG:: LINTER_OUT output:
ERROR:: ( 9, 1): syntax error, unexpected '<', expecting $end
TOTAL:: Errors: 1 Warnings: 0
number: 9
Offset: 0
Token + offset: 9
New Line: ERROR:: ( 9, 1): syntax error, unexpected '<', expecting $end
SublimeLinter: lslint output:
ERROR:: ( 9, 1): syntax error, unexpected '<', expecting $end
TOTAL:: Errors: 1 Warnings: 0
SublimeLinter: error in SublimeLinter daemon:
SublimeLinter: --------------------
SublimeLinter: Traceback (most recent call last):
File "C:\portable\Editors\Sublime Text 3\Data\Packages\SublimeLinter\lint\queue.py", line 57, in loop
item = self.q.get(block=True, timeout=self.MIN_DELAY)
File "./python3.3/queue.py", line 175, in get
queue.Empty
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\portable\Editors\Sublime Text 3\Data\Packages\SublimeLinter\lint\queue.py", line 65, in loop
self.lint(view_id, timestamp)
File "C:\portable\Editors\Sublime Text 3\Data\Packages\SublimeLinter\lint\queue.py", line 111, in lint
self.callback(view_id, timestamp)
File "C:\portable\Editors\Sublime Text 3\Data\Packages\SublimeLinter\sublimelinter.py", line 120, in lint
Linter.lint_view(view, filename, code, hit_time, callback)
File "C:\portable\Editors\Sublime Text 3\Data\Packages\SublimeLinter\lint\linter.py", line 957, in lint_view
linter.lint(hit_time)
File "C:\portable\Editors\Sublime Text 3\Data\Packages\SublimeLinter\lint\linter.py", line 1494, in lint
start, end = self.highlight.full_line(line)
File "C:\portable\Editors\Sublime Text 3\Data\Packages\SublimeLinter\lint\highlight.py", line 200, in full_line
start = self.newlines[line] + char_offset
IndexError: list index out of range
SublimeLinter: --------------------
That's an mcpp warning. Your include file doesn't seem to end in newline. That seems to be a problem in the specifications for includes in the ANSI C preprocessor. Boost::Wave, the preprocessor used in Firestorm, rejects those right away, stopping preprocessing and causing many duplicate issues in JIRA for that reason. mcpp seems to be gentler and reports a warning instead.
You can disable mcpp warnings with -W0
.
Edit: No idea about the queue.Empty
exception.
So far, it doesn't work with scripts containing large amounts of preprocessor directives. I shall try with smaller ones.
That makes little sense. Any example I can examine myself?
@line 1216:
- string text = g_HoverText;
+ string text = g_HoverText;xdzfsdf
It does output something, however visual hints are non-functional somewhere else
DEBUG:: LINTER_OUT output:
ERROR:: (896, 9): syntax error, unexpected IDENTIFIER
WARN:: (894, 16): variable `text' declared but never used.
TOTAL:: Errors: 1 Warnings: 1
number: 896
Offset: -331
Token + offset: 565
New Line: ERROR:: (565, 9): syntax error, unexpected IDENTIFIER
number: 894
Offset: -323
Token + offset: 571
New Line: WARN:: (571, 16): variable `text' declared but never used.
=== END LINTER DEBUG ===
SublimeLinter: lslint output:
ERROR:: (565, 9): syntax error, unexpected IDENTIFIER
WARN:: (571, 16): variable `text' declared but never used.
TOTAL:: Errors: 1 Warnings: 1
Why tokminoff = number + int(offset)
? It used to be minus, and I don't think it works with plus.
Also, I strongly recommend against iter_line.replace
.
Why tokminoff = number + int(offset)? It used to be minus, and I don't think it works with plus.
it worked better that way? I'm way out of my league here..
Also, I strongly recommend against iter_line.replace.
What else am I supposed to do? It doesn't modify the original string..
a) It's not the right calculation.
b) A regex replacement would be best, but if you're not confident with them, recompose the string from the parts you separated earlier. Put them back together by adding the WARN:: (
or ERROR:: (
then the new number then the rest of the line.
A regex replacement would be best, but if you're not confident with them, recompose the string from the parts you separated earlier. Put them back together by adding the
WARN:: (
orERROR:: (
then the new number then the rest of the line.
I purposefully avoided regex due to CPU time cost.
Don't avoid them. it's probably more costly to perform so many splits and object creation and destruction anyway. We're talking one regex compilation per lint, and one compiled regex match per very short line. Negligible compared to the time that lslint takes to run.
Going back to this:
ORIGINAL_CODE:
0 |//
1 |//
2 |//
3 |//
4 |//
5 |//
6 |#include "x.h"
7 |default{timer(){error1;
8 |error2;
9 |error3;}}
MCPP Output:
0 |#line 1 "<stdin>"
1 |#line 7 "<stdin>"
2 |#line 1 "x.h"
3 |echo "x(){}"
4 |#line 8 "<stdin>"
5 |default{timer(){error1;
6 |error2;
7 |error3;}}
DEBUG:: LINTER_OUT output:
ERROR:: ( 4, 6): syntax error, unexpected STRING_CONSTANT, expecting '('
TOTAL:: Errors: 1 Warnings: 0
number: 4
Offset: 2
Token + offset: 6
New Line: ERROR:: ( 6, 6): syntax error, unexpected STRING_CONSTANT, expecting '('
SublimeLinter: lslint output:
ERROR:: ( 6, 6): syntax error, unexpected STRING_CONSTANT, expecting '('
TOTAL:: Errors: 1 Warnings: 0
The error was presumably on line 1 of the include, right? However 4 - 2 is still 2, not 1, so maybe you have to apply +2 instead of +1 inside getLastOffset.
@Sei-Lisa That helped, but the line numbers are still wrong on my long script :( EDIT: Not exactly wrong, simply not matching the editor's view:
892 | }
893 | string text = g_HoverText;xdzfsdf
894 |#line 1226 "<stdin>"
895 | llSetText(text+"\n \n \n \n ", <0.825,0.825,0.825> , 0.75 );
896 | llSetTimerEvent(10);
897 | }
898 |}
DEBUG:: LINTER_OUT output:
ERROR:: (896, 9): syntax error, unexpected IDENTIFIER
WARN:: (894, 16): variable `text' declared but never used.
TOTAL:: Errors: 1 Warnings: 1
Ah, I see, it's not putting the right line number back.. D:
I don't understand, but whatever. b92ee10e5f7634e717f82195eb71684f59610e63
Ah, I see, it's not putting the right line number back.. D:
I can't help with that. I have no idea what's the input file or any other debug info. The script you pasted gives no errors for me. I guess you're forcing some error somewhere, but no idea where or what the output should be and what it actually is.
This should work.
I can't even begin to imagine how that abs()
can be correct in any possible case. Could you give an example that I can reproduce?
Edit: Specifically, I don't see how it can be an improvement in any case, beyond very specific cases similar to the specific time of the day where a stopped clock a clock running backwards gives the correct time.
tl;dr version: revert dc92a55 and change the sense of the comparison in getLastOffset
from <=
to >=
.
Long version that I had written when that landed on me:
Let me try to put it this way. Let's for now assume that all line numbers are zero-based, even those coming from lslint, even if it's not true. We need to operate in common units.
a | #line b "<stdin>"
a+1 | something
a+2 | something else
c=a+3 | error point
What #line
is saying is that a+1
should be reported as b
, a+2
should be reported as b+1
, a+3
should be reported as b+2
, and so on.
When lslint reports an error, the error is reported at c=a+3
, the line number in the preprocessed file that has the #line
directives. If you have a
(the line where the last #line
is), b
(the parameter of #line
) and c
(the line where the error is), how do we get and report e=b+2
, the line in the file prior to preprocessing, that corresponds with line a+3
in the preprocessed file?
To do that, you need d=c-(a+1)
, the distance between the error line and the line that the last #line
directive refers to, because that's how much to add to the actual error position vs. if the error had happened in the a+1
line. So we have:
d=c-(a+1) (distance)
e=b+d (line number corresponding to `a+1` plus distance)
Now, the fact that both b
and c
are 1-based rather than 0-based, prompts the need for further corrections:
c
, but at c+1
(one more than the zero-based line).#line
directive is one more than the zero-based line corresponding to a+1
.e
once we find it, because we've transformed everything to 0-based.To compensate for (1), we have the real line coming from lslint, f=c+1
. Solving for c
we get that c=f-1
. Replacing c
in the distance formula we get: d=(f-1)-(a+1)
. Removing parentheses we get: d=f-a-2
.
The line in the #line
directive is not actually b
. It's 1-based, while we assumed that b
is 0-based. To account for that, which is issue (2) above, let's call the actual parameter to the #line
directive g
, where g=b+1
, and from that, b=g-1
.
Now we can replace the variables in the original calculation, e=b+d
, to take into account the actual inputs f
and g
. Replacing b=g-1
we get: e=(g-1)+d
and replacing d=f-a-2
we get e=(g-1)+(f-a-2)
. Removing parentheses: e=g+f-a-3
. We still need to report the 1-based line, h
to account for (3), because e
is 0-based, so I'll skip intermediate steps and add 1 to get the final result, which is: h=g+f-a-2
.
To recap, the line to report, h
, equals the line specified in the #line
directive, g
, plus the lslint error line, f
, minus the zero-based line where #line
is, a
, minus 2.
You are grouping things a bit differently: you are calculating h=f-(a-g+2)
which gives the exact same result after removing parentheses. That's your result = this_tuple.mcpp_in_line - this_tuple.orig_line + 2
when converting it to your naming. While it confused me a bit at the beginning, that has advantages. First, you can separate the calculation of (a-g+2)
into its own function, that searches for the last appearance of c < a
when given c=f-1
(the number - 1
in your call to getLastOffset
). Second, if there is no #line
directive before the given one, you return 0 instead, which means that in that case, h=f+0
(output reported line = input reported line) and the result is still correct.
That should work in every circumstance. There is no reason it shouldn't, unless mcpp has a bug, which I find hard to believe.
But calculating h'=f+abs(a-g+2)
makes no sense at all, no matter how I look at it. That hack is going to bite you at some point for sure, because if there are remaining bugs, they are not in the math for sure.
And, while reviewing my post before submitting, I've realized that you break when this_tuple.mcpp_in_line >= inlined_line
, i.e. you stop when a >= c
aka when c <= a
. That doesn't seem correct. You have to break when c >= a
. The bug is most likely there, but you need to revert dc92a55.
tl;dr version: revert dc92a55 and change the sense of the comparison in
getLastOffset
from<=
to>=
.
That, and reinstate number - 1
when calling getLastOffset
. The - 1
was gone in 2af2961 for some reason.
Scratch that. You must look for the last appearance of a < c
(line with #line
less than error line), so you must break when a >= c
. Your break condition is right. If there is a bug, it must be elsewhere.
@Sei-Lisa That was a lot of math but I think I got the gist of it. I had already figured the logic side of it, I guess I just instinctively avoided doing math where I could 😋
As for why abs()
works: it gave me a correct but negative index when the file is very big. The index is right in small and big files but somehow in my production script it returns a negative version of it, so I just remove that with abs.
I don't know why this happens, I just deal with it.
The code:
if iter_line.startswith("TOTAL::") is False:
tokens = iter_line.split(',')
print('Tokens:[{0}]'.format(tokens))
token = tokens[0]
print("Token:{0}".format(token))
number = int(p.match(token).group(2).strip())
print("number: '{0}'".format(number))
offset = getLastOffset(preproc_bank, number)
print("Offset: {0}".format(offset))
tokminoff = str(number + int(offset))
print("Token + offset: {0}".format(tokminoff))
new_line = re.sub(str(number), tokminoff, iter_line)
print("New Line: {0}".format(new_line))
fixed_output_lines.append(new_line)
continue
Without the abs()
call:
LINE:[ WARN:: (894, 16): variable `text' declared but never used.]
Tokens:[[' WARN:: (894', " 16): variable `text' declared but never used."]]
Token: WARN:: (894
number: '894'
Offset: -322
Token + offset: 572
New Line: WARN:: (572, 16): variable `text' declared but never used.
LINE:[TOTAL:: Errors: 1 Warnings: 1]
With the abs()
call:
LINE:[ WARN:: (894, 16): variable `text' declared but never used.]
Tokens:[[' WARN:: (894', " 16): variable `text' declared but never used."]]
Token: WARN:: (894
number: '894'
Offset: 322
Token + offset: 1216
New Line: WARN:: (1216, 16): variable `text' declared but never used.
LINE:[TOTAL:: Errors: 1 Warnings: 1]
I can't help with that. I have no idea what's the input file or any other debug info. The script you pasted gives no errors for me. I guess you're forcing some error somewhere, but no idea where or what the output should be and what it actually is.
https://github.com/XenHat/SublimeLinter-contrib-lslint/issues/6#issuecomment-336270734
number: '894' Offset: -322 Token + offset: 572
You were not supposed to add it, you were supposed to subtract it. It's what it did in the beginning.
Token - offset = 894 - (-322) = 894 + 322 = 1216
It works.
Edit: "Offset" can be negative. When the line that contains #line
is less than the parameter of #line
, it will have a sign, and when it's greater, it will have the opposite sign. Nothing wrong with that. And with the abs, one of the cases will fail.
Two cases where the offset has opposite signs:
#if 0
#endif
default{timer(){x;}}
#include "long.lsl"
default{timer(){x;}}
list y = [
0,
1,
2,
3,
4,
5,
6,
7];
One of them will fail unless you revert dc92a55. Without the - 1 that was removed in 2af2961, it may fail when the error line is very close to a #line
directive (one before or one after, not sure).
Edit: I used the same variable in case2.lsl as in long.lsl, so case2 won't have errors :( Edited to force an error.
the -1
that was removed in 2af2961 was put back in the function, but somehow that change got lost somewhere. grrrrrrr.. ðŸ˜
Yay! :+1:
What can be done about an error in a different file? Can it notify the main linter module, in order to automatically open the right file where the error is and report it? I really hope so.
If not, then I can only think of two alternatives. The ugliest is to just filter out the errors that correspond to a different file. The least bad is to try to guess where the #include
was, and report the error in it. I have ideas for how to implement the latter, but they will complicate the code.
Edit: Sorry that I was so stubborn. :blush:
RE:edit: No worries. I was too. 😊
About complexity: I'm not afraid of complexity with logic, but I'm pretty trash at math.
About the file thing... well, it would be easier if I could get the name of the file opened in the view, but no matter how I look at it, it seems impossible to get code that works fine in the editor's built-in console to behave properly in my linter plugin. I'm not sure why, either.
Calls to file_name()
from any of the two supported classes (windowcommand
and textcommand
just fail. I can't get anything "deeper" than getView then it just breaks. So I guess that's a nope.
However, we can probably treat "none" as "this file" and then do something with the lines coming from another file such as putting the marker at the position of the #include
?
EDIT: if you would prefer to chat about this outside the bug tracker, you can hit me up in-world: https://my.secondlife.com/xenhat.liamano (or secondlife:///app/agent/f1a73716-4ad2-4548-9f0e-634c7a98fe86/im
)
Well, if anything, to not spam Winterwolf with technical details. But since I didn't find you online...
The question was not if you can access the filename. It's assumed that the main file is open in the editor, therefore you don't need to find its name in principle; you can assume that "<stdin>"
means the main file and anything else means the file specified in the #line
directive.
The question was if you can force the linter plugin to open a filename you give, in order to report errors in it. I presume that a C linter, for example, must be able to do exactly this, in order to report problems in the headers. Or any other language that supports includes, for the matter.
If it doesn't support it, maybe you can file a feature request asking to implement that.
But in case it's not supported, here's how I imagine the automatic detection of #include
.
The basis of idea is that the preprocessor will insert a #line
directive with a different filename at the exact point where it found the #include
. Therefore, when an error is found in a different filename, your aim is to find the last #line
directive which had "<stdin>"
as a file before that line (call that L1), keeping track of where the first #line
directive which changed the filename was (call that L2).
Then you replace the error line with L2, and do the same conversion math you're doing now using L1, and that's the line number that will appear in the final output.
Example:
1 | string s; // just to add something
2 | #include "level1.lsl"
3 | default{state_entry(){llOwnerSay(s);}}
1 | #include "level2.lsl"
1 | integer x;
The goal is to report the unused identifier at line 2 of our file, which is the line with the include. The mcpp output is:
1 | #line 1 "<stdin>"
2 | string s;
3 | #line 2 "<stdin>"
4 | #line 1 "level1.lsl"
5 | #line 1 "level2.lsl"
6 | integer x;
7 | #line 2 "level1.lsl"
8 | #line 3 "<stdin>"
9 | default{state_entry(){}}
The error lines reported by lslint are 2 and 6. For line 2, the last #line
had "<stdin>"
as file, therefore you do your usual thing.
For line 6, the last file was not "<stdin>"
, so you have to make a replacement. The last #line
directive that had "<stdin>"
is line 3, and the first #line
directive after that one is line 4. Therefore, you replace the error line number as if it actually happened in line 4, because that's where the #include
was (which is why the filename has changed). Then you do the usual thing, but using 4 as the error line number.
If all goes well, the output should be:
WARN:: ( 1, 8): variable `s' declared but never used.
WARN:: ( 2, 8): variable `t' declared but never used.
TOTAL:: Errors: 0 Warnings: 2
linter.py
at f01e33f thinking out loud:currently not used
You can require
v1.0.6
and use--version
now...
The preference here changed comparing to a few days ago. Maybe eventually it would make sense to compare the version for each and decide based upon that?
no need to
.strip()
'...%s' % ...
or '...%s...%s' % (..., ...)
will format the output to string as well '...{}...{}'.format(..., ...)
. Haven't found a place where usages are compared in a way that would give a reason for either of these. Currently Sublime Text uses Python 3.3, would be nice to be able to use formatted string literals from Python 3.6.1-3: Fixed
4: git
is being a git
5: I like to prepare for the worst
6: read the docs, %
is deprecated
7: I had to.
Here's one (lazy) idea for solving the appending of info:
...
# print("Offset: {0}".format(offset))
# tokminoff = str(number - int(offset))
tokminoff = str(number - int(offset))
# moved from below
# print("Token - offset: {0}".format(tokminoff))
new_line = re.sub(str(number), tokminoff, iter_line)
if result[1] != '"<stdin>"':
index = getLastStdin(preproc_bank, number)
# assert index != -1
new_number = preproc_bank[index + 1].mcpp_in_line + 1
offset = getLastOffset(preproc_bank, new_number)[0]
tokminoff = str(new_number - int(offset))
# the next line is best moved up to avoid calling p.match() twice
token_match = p.match(token)
new_line = '{0}:: ({1:>3}, 1): in file {2}: {3}'.format(token_match.group(1), tokminoff, result[1], new_line)
# print("New Line: {0}".format(new_line))
fixed_output_lines.append(new_line)
continue
Sorry if I'm distracting, there is errors on linux again:
SublimeLinter: ERROR: could not launch '/usr/bin/mcpp -W0'
SublimeLinter: reason: [Errno 2] No such file or directory: '/usr/bin/mcpp -W0'
SublimeLinter: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/winterwolf/Dropbox/bin:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin
SublimeLinter: ERROR: could not launch ['/home/winterwolf/Dropbox/bin/lslint', '-m', '-i', <property object at 0x7f61fcf2a8e8>]
SublimeLinter: reason: Can't convert 'property' object to str implicitly
SublimeLinter: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/winterwolf/Dropbox/bin:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin
reloading settings Packages/User/lsl.sublime-settings
reloading settings Packages/User/ossl.sublime-settings
Package Control: Skipping automatic upgrade, last run at 2017-10-20 09:10:39, next run at 2017-10-20 10:10:39 or after
/usr/bin/mcpp
exists! It is correct path, but not working :/
Looks like Linux treats the executable path correctly and my hack doesn't work there. My OS broke and I had to reinstall it, so I lost the Linux VM.. this might take a few days :(
Or not ;)
So, if everything goes right, the output of the top.lsl example should look like this:
WARN:: ( 1, 8): variable `s' declared but never used.
WARN:: ( 2, 1): in file "level2.lsl": WARN:: ( 1, 8): variable `t' declared but never used.
TOTAL:: Errors: 0 Warnings: 2
(The lack of a space at the beginning of the second line is because the regex doesn't capture leading space, but that's only cosmetic.)
This would create two warnings, one at line 1 and the other at line 2 of the main file (top.lsl). The first warning would look just as usual. The second warning would point to column 1 of the #include
line that included the file, even if indirectly, because the warning is inside it. The text of the warning would be:
in file "level2.lsl": WARN:: ( 1, 8): variable `t' declared but never used.
which allows the user to go to level2.lsl and check line 1 column 8 manually.
Lacking any support for file arguments in SublimeLinter, I think this is the best that can be done and we can declare this issue as fixed.
@Sei-Lisa I'm only getting one warning.. :(
SublimeLinter: lslint output:
WARN:: ( 1, 1): in file "level2.lsl": WARN:: ( 1, 9): variable `x' declared but never used.
TOTAL:: Errors: 0 Warnings: 1
EDIT: Figured something
My bad, apologies. I changed my top.lsl like this, and forgot to update the message:
1 | string s; // just to add something
2 | #include "level1.lsl"
3 | default{state_entry(){}}
Before that change, there was nothing wrong with line 1, so no warning was emitted for it.
Nah, it was something else ^^ my top already looks like this, cleaning up and commiting
Implemented the best we could given SublimeLinter's limitations.
Here is issue that I've posted on lslint repo few days ago: https://github.com/Makopo/lslint/issues/47. My problem is that your plugin ignores files icluded by preprocessor and produces errors about undeclared variables.