I am using the PlainNotes package and its syntax istead of Plain Text. I would like to add some additional syntax for comments, for example for JS comments (triggered by //). I am trying to edit this file using the suggested code in the demo (Tools > Developer > New Syntax):
...
contexts:
main:
......
# Comments begin with a '//' and finish at the end of the line
- match: '//'
scope: punctuation.definition.comment.example-c
push: line_comment
line_comment:
- meta_scope: comment.line.example-c
- match: $
pop: true
One of the likely problems is that I do not know how to set the scope. I tried using the scope from the Ctrl+Alt+Shift+P shortcut when I select HTML comments, and I looked in the tmLanguage file as well, but I am still stuck
For the purposes of this answer we're going to assume that the match rule that we want to apply to the syntax is the following one:
- match: '//'
scope: punctuation.definition.comment.begin.html
push:
- meta_scope: comment.line.double-slash.html
- match: $\n?
pop: true
This is a more compact version of the one from the sample syntax template in that it uses an anonymous context instead of a named context for the push. The scopes have been tweaked slightly (see the standard scope names used for something like this) and we also optionally match the newline at the end of the comment line to make sure things that should not happen when the cursor is in a comment also don't happen when the cursor is on the end of a comment line.
We'll also assume that the sample note file that we want to test with contains the following text:
This is a single line
This is a paragraph of
text that is spread over
several lines.
With that out of the way, there are a few potential pitfalls that you might be running into here; some of them generic to Syntaxes in general, some of them specific to the PlainNotes package:
Pitfall 1: Match rule placement is important
The order of the match statements in a context specifies their precedence for matching in the syntax. Generally speaking, the first match in the context that matches on the text is the one selected.
If you inject the match rule as the last rule in the main context, nothing happens no matter what you try to comment out. Instead the scope for all of the lines of text will remain as text.html.markdown.note meta.paragraph.markdown.
This is because the syntax rule that detects a paragraph of text occurs closer to the top of the main context than the comment match, so before the comment rule can match the paragraph rule has already matched and consumed that text.
Putting the match rule as the first item in main shows a slightly different problem instead. If you do that, you'll see that if you try to comment out the single line, the scope will change to say that there is a comment there (although visually you can't tell; more on that below). Similarly if you try to comment out the first line of the paragraph, it's scope will also change.
However, trying to comment out the second or third lines in the paragraph have no effect; the scope remains unchanged. The reason for that flows directly into pitfall #2.
Pitfall 2: Syntax contexts are distinct
In a Syntax there are one or more contexts in which syntax rules live. There is always a context named main, which is where the syntax "starts" its matching. In the case of our comment rule there is also a second anonymous context that contains a single match rule that signals the end of the comment.
Each of the contexts is distinct; they have rules that they match against, but only the rules in the active context are considered and all others are ignored; the contexts are distinct.
Our issue here is that one of the last rules in the main context detects what it thinks is the start of a paragraph, and when that happens it does a push into an anonymous context just like our comment rule does.
As soon as that happens, the syntax thinks that it's inside of a paragraph of text and a whole other set of rules apply, one of which is not one that matches comments. This lets us comment the first line because it's not a paragraph yet, but as soon as the paragraph starts a comment can't occur until the paragraph exits.
To solve this problem you need to also include the rules for comments inside of the rules for paragraphs so that it will also apply in there.
To do that, we might define a context of our own that contains only our rules for matching comments:
comments:
- match: '//'
scope: punctuation.definition.comment.begin.html
push:
- meta_scope: comment.line.double-slash.html
- match: $\n?
pop: true
Now as the very first match in main we can include the rules from our comments context so that they apply there:
main:
- include: comments
...
We then also search through the syntax to find the location where the meta.paragraph.markdown scope is applied to find the location where a paragraph is matched and do an include in there to inject our comment rule there as well. Keeping the first pitfall in mind, we put it as the first rule in the anonymous context so that it can consume a comment before anything else:
- match: '^(?=\S|[ ]{1,3})(?![=-]{3,}(?=$))'
push:
- meta_scope: meta.paragraph.markdown
- include: comments
...
With that in place, we can now comment lines of paragraphs and the scope will indicate that it is a comment the way we want.
However we still don't see any visual indication that there is a comment in place, which leads us to the third pitfall.
Pitfall 3: Syntaxes and color schemes work together
The syntax in use and the color scheme in use need to work together in order to provide the syntax highlighting that we expect to see. The syntax applies scopes to the text and the color scheme has rules that apply colors to scopes.
If the syntax doesn't scope something with a rule that the color scheme matches against, no style can be applied; this is what's happening here.
Technically speaking the scopes that our rule is applying are correct and standard as per the link outlined above. In this particular case the issue at hand is that the PlainNotes package includes not only it's own syntaxes but also it's own color schemes.
The color schemes that it applies don't match on the standardized scopes that we're using because as defined by the package author the syntax doesn't generate that scope.
The color schemes that PlainNotes provides has rules for comments, but only for block comments and not for line comments that we're using here. You can see this by using an HTML comment in a notes file and seeing that it visually changes.
The most expedient fix would be to modify the scopes applied by the commenting rules so that they apply the scopes that PlainNotes expects:
comments:
- match: '//'
scope: punctuation.definition.comment.begin.html
push:
- meta_scope: comment.block.html
- match: $\n?
pop: true
With that in place, we can comment single lines or interior lines in paragraphs and get the syntax highlighting that we expect:
Depending on your use case, this may or may not be enough to allow comments in all of the places that you want.
For example, comments are not possible in headings because there is a match rule that matches a heading as a complete line, which consumes the comment token before anything can match it.
Fixing that would require reworking the match rules for headings to work similar to comments (i.e. pushing a context so that you can include comments) which may or may not be worth it.
Related
Summary
I'm not asking your opinion about the proposed change being good, bad, amazing, or what.
I'm just asking your help to answer this question: does the change I propose for the syntax coloring of the comments break the syntax coloring of something else (keywords, strings, syntax errors, ...)?
The question above is not opinion based, as my change either does break something or it doesn't. That's it.
Original question
I have created the issue #5876 on Vim's GitHub page to propose a change to vim/runtime/syntax/sed.vim, but it has not received much attention, so I'm considering creating a PR for the change.
In fact, I created an issue instead of a PR because I'm not totally confident the change is not disruptive, hence this question.
The issue is with line 20:
syn match sedComment "^\s*#.*$"
because of which only "full line" comments are colored as comments. Using trailing comments following a command (allowed by GNU sed, for instance), stimulates some red background coloring (since it's considered an error by the syntax coloring logic, I guess).
I think it would be reasonable to relax this definition of comments to permit GNU sed-style comments, for the simple reason that the rule is less restrictive.
In this respect, I have noticed that changing that line to
syn match sedComment "\s*#.*$"
i.e. just removing the anchor ^, seems to be enough. I have also tried testing it by putting some # in search and replace strings in a sed script, and it seems fine.
However I don't feel confident with Vim syntax coloring files, so I would like to be sure that regex, as I edited it, is not causing false positives.
To demonstrate why I'm not confident about it, take this single-line sed script
s/aaa/bbb/#ccc
here # is not colored as a comment, and ccc's background is red (like an error?), whereas, just adding a space, give the correct coloring:
s/aaa/bbb/ #ccc
Therefore I think that my edit works (or seems to work) because of precedence rules between the several syntax coloring directives (with respect to this specific example with s/aaa/bbb/#ccc, I think that # just after the closing delimiter of the s command has a meaning in the language, but I don't know; the GNU Sed man page doesn't say anything about it).
Edit
Another example suggested in the comments is the following, the syntax coloring of which is not broken by the proposed change
s/#if !defined(\([^)]*)/#ifndef \1/ # with or without this comment is fine
My research
I have eventually found some time to study :h E410 and now I have an answer.
From there I read that
1. Keyword
[...] It will only match with a complete word [...]
2. Match
This is a match with a single regexp pattern.
3. Region
This starts at a match of the "start" regexp pattern and ends with a match with the
"end" regexp pattern. Any other text can appear in between. A "skip" regexp
pattern can be used to avoid matching the "end" pattern.
Since the line I'm going to change uses the syntax 2.,
point 1. is not relevant,
point 3. is relevant, as in general there can be an overlap between the what syn match and syn region match.
(By the way, I verified that syn region uses a lazy regex, probably \_.\{-} looking at some examples afterwards, to match stuff between the start and end regexp patterns.)
Then from :h :syn-priority
An item that starts in an earlier position has priority over items that start in later positions.
The answer
Therefore, changing
syn match sedComment "^\s*#.*$"
to
syn match sedComment "\s*#.*$"
should not introduce any syntax error, the reason being that if a text matching the above regex is not actually a comment (as is the case for e.g. the command s/#/hash/), then it must be preceded by some non-comment syntax which will be matched by other syn match/syn region groups. Since this match starts before where the group sedComment starts to match, the former has precedence on the latter.
In conclusion, unless something is broken with sed.vim already, the proposed change will not result in a wrong syntax coloring.
Further observations
Actually, there is already something wrong in sed.vim, and my proposed edit does not solve it (I should edit some other line in sed.vim to fix it, but I'm a bit lazy now).
For instance, in the following line, which is illegal because a is not a valid flag, a does not get the Error coloring.
s/x/y/a
My proposed edit does not solve this bug.
For the same reason, since some other syntax rule is eating 1 character after the third delimiter in the substitution command, the GNU-sed-valid comment in the following command is incorrectly colored
s/x/y/#hello
# │└───┴─── colored as Error
# └─ not colored
The coloring would be wrong even for an old version of sed which does not allow trailing comments, as the Error coloring should include the # too.
My proposed edit does not solve this bug either.
One more observation
The change I propose causes single-letter commands to be highlighted as Error if they are followed by a comment
p # this is a comment
│ └─────────────────┴─── colored as Comment (ok)
└────── colored as Error (bad)
This again, is not a worsening, as the current sed.vim colors almost the whole line above as an Error.
In a Sublime Syntax file, I can color all hard brackets and everything inside using a command like this:
- match: '\[.*?\]'
scope: keyword.control
If I want to color everything inside the bracket a different color, I tried something like this:
- match: '\['
scope: variable.function
comment: Images
push:
- meta_scope: constant.numeric
- match: \]
pop: true
The problem is it will color the last bracket the same color as the inside. Any idea how to make the last bracket the same color as the starting bracket?
In the Syntax documentation, it says this (emphasis mine):
meta_scope. This assigns the given scope to all text within this context, including the patterns that push the context onto the stack and pop it off.
meta_content_scope. As above, but does not apply to the text that triggers the context (e.g., in the above string example, the content scope would not get applied to the quote characters).
In your second example, you're using meta_scope, which causes the match that pops the context off the stack to have this scope applied as well. If you swap it to meta_content_scope, this doesn't happen. However in that case no specific scope is applied to the ] character at all, so it will appear the same as the standard text color.
In order to fix that, you can also apply the same scope to it as to the one that originally pushed the context. For example:
- match: '\['
scope: variable.function
comment: Images
push:
- meta_content_scope: constant.numeric
- match: \]
scope: variable.function
pop: true
Technically if all you're after is the color, just the scope is required and will probably do what you want. However it's "cleaner" to use meta_content_scope as well, to avoid the possibility that it might still be colored based on the wrong scope.
I have a .html file that is working perfectly fine but for some reason Sublime 3 decides that it has invalid code, check the image below:
Any idea why that's happening and how to fix it without having to modify the code?
The HTML5 spec states (my emphasis):
Comments must start with the four character sequence U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS, U+002D HYPHEN-MINUS (<!--). Following this sequence, the comment may have text, with the additional restriction that the text must not start with a single > (U+003E) character, nor start with a U+002D HYPHEN-MINUS character (-) followed by a > (U+003E) character,
nor contain two consecutive U+002D HYPHEN-MINUS characters (--),
nor end with a U+002D HYPHEN-MINUS character (-). Finally, the comment must be ended by the three character sequence U+002D HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN (-->).
So that's why it's complaining. As to how to fix it without changing the code, that's trickier.
Your contention that it works is no different really to C developers wondering why they need to worry about undefined behaviour because the code they wrote works fine. The fact that it works fine in one particular implementation is not relevant to portable code.
My advice is to actually change the code. It's not valid, after all, and any browser (current or future) would be well within its rights to simply reject it.
As an aside after some historical digging, it appears this is not allowed because SGML, on which HTML was based, had slightly different rules regarding comment.
On sensing the <!-- token, the parser was switched to a comment mode where > characters were actually allowed within the comment. If the -- sequence was encountered, it changed to a different mode where the > would end the comment.
In fact, it appears to have been a toggle switch between those two modes, so something like <!-- >>>>> -- xyzzy -- >>>>> --> was possible, but putting a > where the xyzzy would end the comment.
XML, for one, didn't adopt this behaviour and HTML has now modified it to follow the "don't use -- within comments at all" rule, the reason being that hardly anyone knew that the comments behaved in the SGML way, causing some pain :-)
Background
Most style guides recommend keeping line lengths to 79 characters or less. In Haskell, indentation rules mean that expressions frequently need to be broken up with new lines.
Questions:
Within expressions, where is it legal to place a new line?
Is this documented somewhere?
Extended question: I see GHC formatting my code when it reports an error so someone has figured out how to automate the process of breaking long lines. Is there a utility that I can put haskell code into and have it spit that code back nicely formatted?
You can place a newline anywhere between lexical tokens of an expression. However, there are constraints about how much indentation may follow the newline. The easy rule of thumb is to indent the next line to start to the right of the line containing the expression. Beyond that, some style things:
If you are indenting an expression that appears in a definition name = expression, it's good style to indent to the right of the = sign.
If you are indenting an expression that appears on the right-hand side of a do binding or a list comprehension, it's good style to indent to the right of the <- sign.
The authoritative documentation is probably the Haskell 98 Report (Chapter 2 on lexical structure), but personally I don't find this material very easy to read.
I am currently writing a plugin in Vim that needs to highlight arbitrary lines in a file
at the same time.
My approach so far has been implementing match with the line number to highlight it, but the problem is that I would need to add | for every other line in a file and append that information and then call it every time the window gets redrawn.
There is another problem with match in my case, and that is that a line that may not have any whitespace would not look highlighted (match only highlights existing text/whitespace).
So even if I had match rewrite the window and highlighting all the lines I need, from a user's perspective this wouldn't be to useful if the highlighting doesn't show anything if there is no whitespace/text.
Can I get any suggestions in how to effectively show/display/highlight (I'm open to different implementations to solve my problem) arbitrary lines in a file at the same time regardless of amount of text or whitespace?
Edit: My main request is to be able to highlight lines by line number not by regex
matching. So any solution should need to be flexible enough to accept a Line number to match.
Edit: signs is the answer to my problem, and I found this tutorial the best way to grasp and implement what I needed: http://htmlpreview.github.io/?https://github.com/runpaint/vim-recipes/blob/master/text/07_navigation/12_bookmarking_lines_with_visible_markers.html
I would use region rather than match. Here is part of my manuscript syntax file that highlights speech:
:syntax region msSpeech start=/"/ end=/"\|\n\n/
:highlight msSpeech guifg=#000088
It starts with a double quote and ends with another double quote or the end of the paragraph. It will highlight multiple lines if need be.