Skip to content

Commit b8f4168

Browse files
authored
Merge pull request #1366 from pjkaufman/master
Fix CJK Characters at Start of Heading Potentially Getting Starting Space Removed
2 parents 70b2130 + 5fca627 commit b8f4168

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

__tests__/remove-space-around-characters.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,17 @@ ruleTest({
9292
includeCJKSymbolsAndPunctuation: true,
9393
},
9494
},
95+
{ // accounts for https://github.com/platers/obsidian-linter/issues/1280
96+
testName: 'Make sure that heading text is handled properly and keeps the heading as valid',
97+
before: dedent`
98+
## 「example here」 More text here
99+
`,
100+
after: dedent`
101+
## 「example here」More text here
102+
`,
103+
options: {
104+
includeCJKSymbolsAndPunctuation: true,
105+
},
106+
},
95107
],
96108
});

src/rules/remove-space-around-characters.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {Options, RuleType} from '../rules';
22
import RuleBuilder, {BooleanOptionBuilder, ExampleBuilder, OptionBuilderBase, TextOptionBuilder} from './rule-builder';
33
import dedent from 'ts-dedent';
44
import {ignoreListOfTypes, IgnoreTypes} from '../utils/ignore-types';
5-
import {updateListItemText} from '../utils/mdast';
5+
import {updateHeaderText, updateListItemText} from '../utils/mdast';
66
import {escapeRegExp} from '../utils/regex';
77

88
class RemoveSpaceAroundCharactersOptions implements Options {
@@ -53,9 +53,10 @@ export default class RemoveSpaceAroundCharacters extends RuleBuilder<RemoveSpace
5353
return text.replace(fullwidthCharacterWithTextAtStart, '$2').replace(fullwidthCharacterWithTextAtEnd, '$1');
5454
};
5555

56-
let newText = ignoreListOfTypes([IgnoreTypes.list], text, replaceWhitespaceAroundFullwidthCharacters);
56+
let newText = ignoreListOfTypes([IgnoreTypes.list, IgnoreTypes.heading], text, replaceWhitespaceAroundFullwidthCharacters);
5757

5858
newText = updateListItemText(newText, replaceWhitespaceAroundFullwidthCharacters);
59+
newText = updateHeaderText(newText, replaceWhitespaceAroundFullwidthCharacters);
5960

6061
return newText;
6162
}

src/utils/ignore-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const IgnoreTypes: Record<string, IgnoreType> = {
1919
math: {replaceAction: MDAstTypes.Math, placeholder: '{MATH_PLACEHOLDER}'},
2020
inlineMath: {replaceAction: MDAstTypes.InlineMath, placeholder: '{INLINE_MATH_PLACEHOLDER}'},
2121
html: {replaceAction: MDAstTypes.Html, placeholder: '{HTML_PLACEHOLDER}'},
22+
heading: {replaceAction: MDAstTypes.Heading, placeholder: '{HEADING_PLACEHOLDER}'},
2223
// RegExp
2324
yaml: {replaceAction: yamlRegex, placeholder: escapeDollarSigns('---\n---')},
2425
wikiLink: {replaceAction: wikiLinkRegex, placeholder: '{WIKI_LINK_PLACEHOLDER}'},

src/utils/mdast.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ type PositionPlusEmptyIndicator = {
2424
isEmpty: boolean,
2525
}
2626

27+
type PositionPlusText = {
28+
position: Position,
29+
text: string,
30+
}
31+
2732
export enum MDAstTypes {
2833
Link = 'link',
2934
Footnote = 'footnoteDefinition',
@@ -38,6 +43,8 @@ export enum MDAstTypes {
3843
Blockquote = 'blockquote',
3944
HorizontalRule = 'thematicBreak',
4045
Html = 'html',
46+
Heading = 'heading',
47+
Text = 'text',
4148
// math types
4249
Math = 'math',
4350
InlineMath = 'inlineMath',
@@ -149,6 +156,31 @@ function getListItemTextPositions(text: string, includeEmptyNodes: boolean = fal
149156
return positions;
150157
}
151158

159+
function getHeaderTextPositions(text: string): PositionPlusText[] {
160+
const ast = parseTextToAST(text);
161+
const positions: PositionPlusText[] = [];
162+
visit(ast, MDAstTypes.Heading as string, (node) => {
163+
// @ts-ignore the fact that not all nodes have a children property since I am skipping any that do not
164+
if (!node.children || node.children.length === 0) {
165+
return;
166+
}
167+
168+
// @ts-ignore the fact that not all nodes have a children property since I have already exited the function if that is the case
169+
for (const childNode of node.children) {
170+
if (childNode.type === (MDAstTypes.Text as string)) {
171+
positions.push({
172+
position: childNode.position as Position,
173+
text: childNode.value as string,
174+
});
175+
}
176+
}
177+
});
178+
179+
// Sort positions by start position in reverse order
180+
positions.sort((a, b) => b.position.start.offset - a.position.start.offset);
181+
return positions;
182+
}
183+
152184
// mdast helper methods
153185

154186
/**
@@ -1169,3 +1201,30 @@ export function ensureFencedCodeBlocksHasLanguage(text: string, defaultLanguage:
11691201

11701202
return text;
11711203
}
1204+
1205+
export function updateHeaderText(text: string, func:(text: string) => string): string {
1206+
const positions = getHeaderTextPositions(text);
1207+
1208+
// for the best performance, we want to grab all places that need updating and then
1209+
// at the end we want to update the text in one go because otherwise we get a lot of
1210+
// instances of the file text in memory
1211+
const updateLocations: {startIndex: number, endIndex: number, newText: string}[] = [];
1212+
for (const position of positions) {
1213+
const updatedText = func(position.text);
1214+
if (updatedText !== position.text) {
1215+
const headerText = text.substring(position.position.start.offset, position.position.end.offset);
1216+
const startIndex = position.position.start.offset+ headerText.indexOf(position.text);
1217+
updateLocations.push({
1218+
startIndex: startIndex,
1219+
endIndex: startIndex + position.text.length,
1220+
newText: updatedText,
1221+
});
1222+
}
1223+
}
1224+
1225+
for (const headerUpdate of updateLocations) {
1226+
text = replaceTextBetweenStartAndEndWithNewValue(text, headerUpdate.startIndex, headerUpdate.endIndex, headerUpdate.newText);
1227+
}
1228+
1229+
return text;
1230+
}

0 commit comments

Comments
 (0)