Skip to content

Highlighting: Implement slice format for text-only highlight#1336

Merged
ang-zeyu merged 23 commits into
MarkBind:masterfrom
ryoarmanda:code-highlight-text-only
Oct 19, 2020
Merged

Highlighting: Implement slice format for text-only highlight#1336
ang-zeyu merged 23 commits into
MarkBind:masterfrom
ryoarmanda:code-highlight-text-only

Conversation

@ryoarmanda

@ryoarmanda ryoarmanda commented Aug 27, 2020

Copy link
Copy Markdown
Contributor

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:

  • Line text-only highlighting: lineNum.
  • Range text-only highlighting: lineStart-lineEnd.
  • Line whole-line highlighting: Now use lineNumber[:] (called unbounded line-slice)
  • Range whole-line highlighting: Now use lineStart[:]-lineEnd or lineStart-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):

```js {start-from=5 highlight-lines="5,6[:],7-8,9[:]-10"}
function f(x) {
  const foo = 1;
  const bar = 2;
  const baz = 3;
  const square = x * x;
  return square;
}
```

Example view:
image

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 modified markbind.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.

@ryoarmanda ryoarmanda marked this pull request as ready for review August 29, 2020 08:44

@ang-zeyu ang-zeyu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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 the 2 would 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the processing model is getting heavy, let's create explicit class(es) for the intermediate processing result(s)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just remove the long comment from Note: The algorithm ...

this area is hardly a bottleneck in performance

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay, will do

@ryoarmanda

Copy link
Copy Markdown
Contributor Author

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.

Should we use [:] to highlight the entire line instead?

Ah so basically swapping the behaviour of e.g. 2-7 and 2[:]-7 right? I think it's a nice idea but it depends on what the expected common usage would be. For me personally I like to lean more into text-only highlighting as the default, and so reserving [:] just for line highlighting for convenience sake. But also would like to know your input as well @damithc

@ang-zeyu

ang-zeyu commented Sep 8, 2020

Copy link
Copy Markdown
Contributor

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 the 2 would 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)

any thoughts? @damithc

@damithc

damithc commented Sep 8, 2020

Copy link
Copy Markdown
Contributor

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 [s:e] syntax comes into play only if the user wants to do more advanced stuff with highlighting. This is assuming full line highlighting is going to remain the default.

@ang-zeyu

ang-zeyu commented Sep 9, 2020

Copy link
Copy Markdown
Contributor

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?

Agree here

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 [s:e] syntax comes into play only if the user wants to do more advanced stuff with highlighting. This is assuming full line highlighting is going to remain the default.

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 s / e would slice all the way to the start / end, might be small plus to follow this too. (so if you want full line highlighting it would be [:])

@damithc

damithc commented Sep 9, 2020

Copy link
Copy Markdown
Contributor

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 s / e would slice all the way to the start / end, might be small plus to follow this too. (so if you want full line highlighting it would be [:])

Oh, right :-p
OK, I'm sold. Let's do that.

@ryoarmanda ryoarmanda changed the title [WIP] Highlighting: Implement slice format for text-only highlight Highlighting: Implement slice format for text-only highlight Oct 5, 2020

@ang-zeyu ang-zeyu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could the magic numbers be removed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ang-zeyu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good now 👍

Just a couple more nits:

Comment thread docs/userGuide/syntax/code.mbdf Outdated

You can specify highlighting in many different ways, depending on how you want it to be. There are two main variants:

##### Text-only highlighting

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to clarify, what happens if its 10-12[:]?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does the same thing as 10[:]-12 at the moment, highlights full-line.

/**
* @type Array<HighlightRuleComponent>
*/
this.rule = rule;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use plural for arrays

or ruleComponents perhaps

return this.rule.length === 2;
}

static parseRule(ruleString) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@ryoarmanda ryoarmanda Oct 13, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@ryoarmanda ryoarmanda Oct 19, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay then 👍 will be shifted to the bottom of the class


class HighlightRuleComponent {
constructor(lineNumber, isSlice, bounds) {
this.lineNumber = lineNumber;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice cleanup here 👍

}

offsetLines(offset) {
this.rule.map(comp => comp.offsetLineNumber(offset));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forEach

return HighlightRule._highlightTextOnly(line);
}

static _splitCodeAndIndentation(codeStr) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's keep related methods together; i.e. below _highlightTextOnly

@ang-zeyu ang-zeyu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@ang-zeyu ang-zeyu merged commit c5f79a1 into MarkBind:master Oct 19, 2020
wxwxwxwx9 pushed a commit to wxwxwxwx9/markbind that referenced this pull request Nov 1, 2020
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Code highlighting: don't highlighting leading/trailing empty space

3 participants