Skip to content

Commit

Permalink
Ignore Links for Rules (#200)
Browse files Browse the repository at this point in the history
* added an event when you right click a folder to allow for linting that folder (the underlying code duplicates the confirmation modal code and slightly modifies the linting rule for all files for just the folder)

* fixed eslint issues

* added command for the action

* updated readme, made sure I could reuse the modal, and added a documentation image showing the ability to get the lint folder option in the right click menu

* refactored the modal to work with the two different linting options that use it

* added UTs and logic to not lint links

* changed order to make sure yaml is taken out first and then the links and fixed the wiki link test to actually be a wiki link
  • Loading branch information
pjkaufman authored May 13, 2022
1 parent 270563c commit 678a612
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Pull requests are welcome, especially for new rules.
1. Insert a new rule in the corresponding rule type(spacing, headings, etc)
2. Follow the format of the existing rules
3. Add tests for edge cases in `test.ts`
4. You should probably use some helper functions from `utils.ts`, such as `ignoreCodeBlocksAndYAML`.
4. You should probably use some helper functions from `utils.ts`, such as `ignoreCodeBlocksYAMLAndLinks`.
5. Run `npm run compile` to build, generate documentation, and test the plugin.
6. Run `npm run lint` to lint the plugin.

Expand Down
2 changes: 1 addition & 1 deletion docs/readme_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Pull requests are welcome, especially for new rules.
1. Insert a new rule in the corresponding rule type(spacing, headings, etc)
2. Follow the format of the existing rules
3. Add tests for edge cases in `test.ts`
4. You should probably use some helper functions from `utils.ts`, such as `ignoreCodeBlocksAndYAML`.
4. You should probably use some helper functions from `utils.ts`, such as `ignoreCodeBlocksYAMLAndLinks`.
5. Run `npm run compile` to build, generate documentation, and test the plugin.
6. Run `npm run lint` to lint the plugin.

Expand Down
42 changes: 21 additions & 21 deletions src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
escapeDollarSigns,
formatYAML,
headerRegex,
ignoreCodeBlocksAndYAML,
ignoreCodeBlocksYAMLAndLinks,
initYAML,
insert,
loadYAML,
Expand Down Expand Up @@ -183,7 +183,7 @@ export const rules: Rule[] = [
'Removes extra spaces after every line.',
RuleType.SPACING,
(text: string, options = {}) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
if (options['Two Space Linebreak'] === false) {
return text.replace(/[ \t]+$/gm, '');
} else {
Expand Down Expand Up @@ -228,7 +228,7 @@ export const rules: Rule[] = [
'All headings have a blank line both before and after (except where the heading is at the beginning or end of the document).',
RuleType.SPACING,
(text: string, options = {}) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
if (options['Bottom'] === false) {
text = text.replace(/(^#+\s.*)\n+/gm, '$1\n'); // trim blank lines after headings
text = text.replace(/\n+(#+\s.*)/g, '\n\n$1'); // trim blank lines before headings
Expand Down Expand Up @@ -295,7 +295,7 @@ export const rules: Rule[] = [
'All paragraphs should have exactly one blank line both before and after.',
RuleType.SPACING,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
text = text.replace(/\n+([a-zA-Z].*)/g, '\n\n$1'); // trim blank lines before
text = text.replace(/(^[a-zA-Z].*)\n+/gm, '$1\n\n'); // trim blank lines after
text = text.replace(/^\n+([a-zA-Z].*)/, '$1'); // remove blank lines before first line
Expand Down Expand Up @@ -325,7 +325,7 @@ export const rules: Rule[] = [
'There should be a single space after list markers and checkboxes.',
RuleType.SPACING,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
// Space after marker
text = text.replace(/^(\s*\d+\.|\s*[-+*])[^\S\r\n]+/gm, '$1 ');
// Space after checkbox
Expand Down Expand Up @@ -391,7 +391,7 @@ export const rules: Rule[] = [
'There should be at most one consecutive blank line.',
RuleType.SPACING,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
return text.replace(/\n{2,}/g, '\n\n');
});
},
Expand All @@ -417,7 +417,7 @@ export const rules: Rule[] = [
'Converts leading spaces to tabs.',
RuleType.SPACING,
(text: string, options = {}) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
const tabsize = String(options['Tabsize']);
const tabsize_regex = new RegExp(
'^(\t*) {' + String(tabsize) + '}',
Expand Down Expand Up @@ -501,7 +501,7 @@ export const rules: Rule[] = [
'Removes two or more consecutive spaces. Ignores spaces at the beginning and ending of the line. ',
RuleType.CONTENT,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
return text.replace(/\b {2,}\b/g, ' ');
});
},
Expand All @@ -522,7 +522,7 @@ export const rules: Rule[] = [
'Removes hyphenated line breaks. Useful when pasting text from textbooks.',
RuleType.CONTENT,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
return text.replace(/\b[-] \b/g, '');
});
},
Expand All @@ -543,7 +543,7 @@ export const rules: Rule[] = [
'Removes consecutive list markers. Useful when copy-pasting list items.',
RuleType.CONTENT,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
return text.replace(/^([ |\t]*)- - \b/gm, '$1- ');
});
},
Expand Down Expand Up @@ -572,7 +572,7 @@ export const rules: Rule[] = [
'Removes empty list markers, i.e. list items without content.',
RuleType.CONTENT,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
return text.replace(/^\s*-\s*\n/gm, '');
});
},
Expand All @@ -596,7 +596,7 @@ export const rules: Rule[] = [
'Converts common bullet list marker symbols to markdown list markers.',
RuleType.CONTENT,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
// Convert [•, §] to - if it's the first non space character on the line
return text.replace(/^([^\S\n]*)([§])([^\S\n]*)/gm, '$1-$3');
});
Expand Down Expand Up @@ -633,7 +633,7 @@ export const rules: Rule[] = [
'Replaces three consecutive dots with an ellipsis.',
RuleType.CONTENT,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
return text.replaceAll('...', '…');
});
},
Expand Down Expand Up @@ -913,7 +913,7 @@ export const rules: Rule[] = [
RuleType.YAML,
(text: string, options = {}) => {
text = initYAML(text);
let title = ignoreCodeBlocksAndYAML(text, (text) => {
let title = ignoreCodeBlocksYAMLAndLinks(text, (text) => {
const result = text.match(/^#\s+(.*)/m);
if (result) {
return result[1];
Expand Down Expand Up @@ -981,7 +981,7 @@ export const rules: Rule[] = [
'Heading levels should only increment by one level at a time',
RuleType.HEADING,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
const lines = text.split('\n');
let lastLevel = 0; // level of last header processed
let decrement = 0; // number of levels to decrement following headers
Expand Down Expand Up @@ -1035,7 +1035,7 @@ export const rules: Rule[] = [
'Inserts the file name as a H1 heading if no H1 heading exists.',
RuleType.HEADING,
(text: string, options = {}) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
// check if there is a H1 heading
const hasH1 = text.match(/^#\s.*/m);
if (hasH1) {
Expand Down Expand Up @@ -1086,7 +1086,7 @@ export const rules: Rule[] = [
'Headings should be formatted with capitalization',
RuleType.HEADING,
(text: string, options = {}) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
const lines = text.split('\n');
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(headerRegex); // match only headings
Expand Down Expand Up @@ -1242,7 +1242,7 @@ export const rules: Rule[] = [
'Move all footnotes to the bottom of the document.',
RuleType.FOOTNOTE,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
return moveFootnotesToEnd(text);
});
},
Expand Down Expand Up @@ -1276,7 +1276,7 @@ export const rules: Rule[] = [
'Re-indexes footnote keys and footnote, based on the order of occurence (NOTE: This rule deliberately does *not* preserve the relation between key and footnote, to be able to re-index duplicate keys.)',
RuleType.FOOTNOTE,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
// re-index footnote-text
let ft_index = 0;
text = text.replace(/^\[\^\w+\]: /gm, function() {
Expand Down Expand Up @@ -1349,7 +1349,7 @@ export const rules: Rule[] = [
'Ensures that footnote references are placed after punctuation, not before.',
RuleType.FOOTNOTE,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
// regex uses hack to treat lookahead as lookaround https://stackoverflow.com/a/43232659
// needed to ensure that no footnote text followed by ":" is matched
return text.replace(/(?!^)(\[\^\w+\]) ?([,.;!:?])/gm, '$2$1');
Expand All @@ -1372,7 +1372,7 @@ export const rules: Rule[] = [
'Ensures that Chinese and English or numbers are separated by a single space. Follow this [guidelines](https://github.com/sparanoid/chinese-copywriting-guidelines)',
RuleType.SPACING,
(text: string) => {
return ignoreCodeBlocksAndYAML(text, (text) => {
return ignoreCodeBlocksYAMLAndLinks(text, (text) => {
const head = /([\u4e00-\u9fa5])( *)(\[[^[]*\]\(.*\)|`[^`]*`|\w+|[-+'"([{¥$]|\*[^*])/gm;
const tail = /(\[[^[]*\]\(.*\)|`[^`]*`|\w+|[-+;:'"°%)\]}]|[^*]\*)( *)([\u4e00-\u9fa5])/gm;
return text.replace(head, '$1 $3').replace(tail, '$1 $3');
Expand Down
48 changes: 48 additions & 0 deletions src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,3 +674,51 @@ describe('YAML Title', () => {
expect(rulesDict['yaml-title'].apply(before, {'Title Key': 'title'})).toBe(after);
});
});

describe('Links', () => {
it('Regular link with spaces stays the same', () => {
const before = dedent`
# Hello world
[This has spaces in it](File with spaces.md)
`;

const after = dedent`
# Hello world
[This has spaces in it](File with spaces.md)
`;

expect(rulesDict['trailing-spaces'].apply(before)).toBe(after);
});
it('Image link with spaces stays the same', () => {
const before = dedent`
# Hello world
![This has spaces in it](File with spaces.png)
`;

const after = dedent`
# Hello world
![This has spaces in it](File with spaces.png)
`;

expect(rulesDict['trailing-spaces'].apply(before)).toBe(after);
});
it('Wiki link with spaces stays the same', () => {
const before = dedent`
# Hello world
[[File with spaces]]
`;

const after = dedent`
# Hello world
[[File with spaces]]
`;

expect(rulesDict['trailing-spaces'].apply(before)).toBe(after);
});
});
16 changes: 13 additions & 3 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const backtickBlockRegexTemplate = fencedRegexTemplate.replaceAll('X', '`
export const tildeBlockRegexTemplate = fencedRegexTemplate.replaceAll('X', '~');
export const indentedBlockRegex = '^((\t|( {4})).*\n)+';
export const codeBlockRegex = new RegExp(`${backtickBlockRegexTemplate}|${tildeBlockRegexTemplate}|${indentedBlockRegex}`, 'gm');

export const linkRegex = /((!?)\[.*\]\(.*\))|(\[{2}.*\]{2})/g;

// Helper functions

Expand Down Expand Up @@ -96,14 +96,14 @@ export function moveFootnotesToEnd(text: string) {
}

/**
* Substitutes YAML and codeblocks in a text with a placeholder.
* Substitutes YAML, links, and codeblocks in a text with a placeholder.
* Then applies the given function to the text.
* Substitutes the YAML and codeblocks back to their original form.
* @param {string} text - The text to process
* @param {function(string): string} func - The function to apply to the text
* @return {string} The processed text
*/
export function ignoreCodeBlocksAndYAML(text: string, func: (text: string) => string): string {
export function ignoreCodeBlocksYAMLAndLinks(text: string, func: (text: string) => string): string {
const codePlaceholder = 'PLACEHOLDER 321417';
const ret = replaceCodeblocks(text, codePlaceholder);
text = ret.text;
Expand All @@ -115,8 +115,18 @@ export function ignoreCodeBlocksAndYAML(text: string, func: (text: string) => st
text = text.replace(yamlMatches[0], escapeDollarSigns(yamlPlaceholder));
}

const linkPlaceHolder = 'PLACEHOLDER_LINK 57849';
const linkMatches = text.match(linkRegex);
text = text.replaceAll(linkRegex, linkPlaceHolder);

text = func(text);

if (linkMatches) {
for (const link of linkMatches) {
text = text.replace(linkPlaceHolder, link);
}
}

if (yamlMatches) {
text = text.replace(yamlPlaceholder, escapeDollarSigns(yamlMatches[0]));
}
Expand Down

0 comments on commit 678a612

Please sign in to comment.