Highlighting: Implement slice format for text-only highlight#1336
Conversation
ang-zeyu
left a comment
There was a problem hiding this comment.
@ryoarmanda I saw that you tagged it ready for review but the [wip] tag is still there; If it's still a wip do ignore these where applicable:
- Code quality can be generally improved with some processing model(s) / class(es). (e.g.
ruleComponents) - Should we use
[:]to highlight the entire line instead? @damithc @ryoarmanda
Reason: since we are going with stripping trailing/leading spaces by default (or not?)
Also, when we have[2:5]eventually the2would be counted from the start of the line, including whitespaces. (but this may actually be undesirable versus counting from the first non-whitespace character, which should be easier for the author)
| // it's more concise to write a for-loop so that we can perform both in one block. | ||
| for (let i = 0; i < highlightRules.length; i++) { | ||
| const rule = highlightRules[i]; | ||
| const [a, b, c] = rule; // can be up to three items |
There was a problem hiding this comment.
since the processing model is getting heavy, let's create explicit class(es) for the intermediate processing result(s)
There was a problem hiding this comment.
Oh yes, that would be nicer. I'll create a separate class file for the rule components and its helpers (e.g. isLineSlice)
| const inRange = lineNumbersAndRanges.some(([start, end]) => { | ||
| if (start && end) { | ||
| return currentLineNumber >= start && currentLineNumber <= end; | ||
| // Edit (28/8/2020): I changed the approach from using some() to a simple for-loop. It is still O(n^2). |
There was a problem hiding this comment.
let's just remove the long comment from Note: The algorithm ...
this area is hardly a bottleneck in performance
There was a problem hiding this comment.
Ah, okay, will do
|
Thanks for the review @ang-zeyu 👍 I'm still marking it WIP as I haven't updated the docs yet. Will remove it once it's done and have addressed the reviews.
Ah so basically swapping the behaviour of e.g. |
any thoughts? @damithc |
|
I think the first thing to decide is if slice positions should include leading spaces or not. pros of omitting leading spaces from counting: easier to count for authors cons: not possible to start highlighting somewhere in the middle of leading spaces e.g., if I wanted to show how line wrapping requires 8 spaces for indentation, I may want to highlight the 8 leading spaces in a line to illustrate that. As this is an advance feature not expected to be used frequently, perhaps we choose the more flexible option so that even rare cases become possible? Regarding the use of [:], the advantage of using it to highlight the trimmed line (not the full line) is that the current syntax remains the same. The |
Agree here
Wasn't #1311 to change the default behaviour of full line highlighting? 😮 In python (im assuming that's where the inspiration for this came from) omitting either |
Oh, right :-p |
ang-zeyu
left a comment
There was a problem hiding this comment.
Nice work extracting out the logic, let's try to remove the use of magic numbers next too:
|
|
||
| isUnboundedSlice() { | ||
| return this.isSlice() | ||
| && this.components[1] === -1 |
There was a problem hiding this comment.
let's remove the use of magic numbers in favour of named instance properties, so we don't need to refer back to the usage to understand which is which
| shouldApplyHighlight(lineNumber) { | ||
| const compares = this.rule.map(comp => comp.compareLine(lineNumber)); | ||
| if (this.isLineRange()) { | ||
| return (compares[0] <= 0) && (compares[1] >= 0); |
There was a problem hiding this comment.
could the magic numbers be removed?
There was a problem hiding this comment.
but if this is a necessary part of the algorithm, maybe you could use some named constant declarations here, which are self-documenting
const whatIsThis = compares[0]
There was a problem hiding this comment.
Yeah, compareLine is basically emulating what Java's compareTo does, returning either a positive integer, zero, or a negative integer depending on whether the given line number is before, at, or after the component's line number. So I guess it is inevitable to compare whether it's <= 0 like the above to ensure that the line number is within the bounds.
I'll just name these checks as constant declarations.
| } | ||
|
|
||
| isLineSlice() { | ||
| return this.rule.length === 1 && this.rule[0].isSlice(); |
There was a problem hiding this comment.
Since its used only in applyHighlight and is rather simple, perhaps we could just use a named constant instead? (ignore if this is going to be removed when dealing with the below)
ang-zeyu
left a comment
There was a problem hiding this comment.
Mostly looks good now 👍
Just a couple more nits:
|
|
||
| You can specify highlighting in many different ways, depending on how you want it to be. There are two main variants: | ||
|
|
||
| ##### Text-only highlighting |
There was a problem hiding this comment.
could try super / regular bold if h6 dosen't work too well
| <include src="codeAndOutputCode.md" boilerplate > | ||
| <variable name="code"> | ||
| ```java {highlight-lines="2,4,6-8"} | ||
| ```java {highlight-lines="1,3[:],6-8,10[:]-12"} |
There was a problem hiding this comment.
to clarify, what happens if its 10-12[:]?
There was a problem hiding this comment.
It does the same thing as 10[:]-12 at the moment, highlights full-line.
| /** | ||
| * @type Array<HighlightRuleComponent> | ||
| */ | ||
| this.rule = rule; |
There was a problem hiding this comment.
let's use plural for arrays
or ruleComponents perhaps
| return this.rule.length === 2; | ||
| } | ||
|
|
||
| static parseRule(ruleString) { |
There was a problem hiding this comment.
hmm is there a pattern to the organization of methods?
maybe we could shift this and the components counterpart below right below their constructors since its also somewhat of a constructor
There was a problem hiding this comment.
I wrote it in the order of usage flow: parsing of the rule, post-parse processing (offset line numbers), test for application, and lastly the application of the rule itself. I feel like this way is intuitive if you want to read the whole class and understand the sequence of process.
Although, the special, structural checks like isLineSlice (and isUnboundedSlice on the components side) does not fit too well on this scheme. I put it where it was now (on top of everything, after the constructor) if any developers want to understand what are the possible variants of the structure possible for this class before reading deep into the class methods.
Not sure if this is the best way, I don't have a strong preference really. What do you think?
There was a problem hiding this comment.
ok, thanks for the explanation!
How about shifting isLineRange to the bottom at least then? Then we can keep the constructors together (parseRule and constructor) while still fitting to your scheme. Likewise for the Component version
There was a problem hiding this comment.
Okay then 👍 will be shifted to the bottom of the class
|
|
||
| class HighlightRuleComponent { | ||
| constructor(lineNumber, isSlice, bounds) { | ||
| this.lineNumber = lineNumber; |
| } | ||
|
|
||
| offsetLines(offset) { | ||
| this.rule.map(comp => comp.offsetLineNumber(offset)); |
| return HighlightRule._highlightTextOnly(line); | ||
| } | ||
|
|
||
| static _splitCodeAndIndentation(codeStr) { |
There was a problem hiding this comment.
let's keep related methods together; i.e. below _highlightTextOnly
There was a problem hiding this comment.
Sorry, missed it out earlier
Let's add / modify some simple tests to testCodeBlocks.md in the test_site as well, so we can detect unintended changes in the future
Oops
Lgtm, thanks! @ryoarmanda
The highlight from the highlight-lines attribute spans the entire width of the block, which might interfere with the visual structure of the code. Let's add ways for authors to highlight only the text part of the code.
What is the purpose of this pull request? (put "X" next to an item, remove the rest)
• [x] Enhancement to an existing feature
Resolves #1311
What is the rationale for this request?
Line highlighting highlights the full width of the code block, which can interfere with the visual structure of the code. It would be better to highlight only the text portion of the code, ignoring indentations.
What changes did you make? (Give an overview)
Added/updated syntax for line highlighting:
lineNum.lineStart-lineEnd.lineNumber[:](called unbounded line-slice)lineStart[:]-lineEndorlineStart-lineEnd[:](basically either of the ends is an unbounded line-slice)Provide some example code that this change will affect:
Example fenced code (presenting all the line-highlighting capabilities supported):
Example view:

Is there anything you'd like reviewers to focus on?
I'm not sure whether the code organization is proper already. Some of the lines I modified are just following the style of that particular line(s). If you have any suggestions for tidying up, please let me know in the review!
Testing instructions:
markbind serve -d(to compile the modifiedmarkbind.css)Proposed commit message: (wrap lines at 72 characters)
Add slice format for text-only code highlight
The highlight from the highlight-lines attribute spans the entire
width of the block, which might interfere with the visual structure
of the code.
Let's add ways for authors to highlight only the text part of the code.