This commit is contained in:
meganrogge 2020-12-01 17:03:25 -08:00
parent 9dba02c075
commit 5e54da666c
2 changed files with 57 additions and 57 deletions

View file

@ -131,9 +131,17 @@ function createInlineRange(document: vscode.TextDocument, cursorPosition: vscode
const lineText = document.lineAt(cursorPosition.line).text;
const boldSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, parent);
const italicSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, parent);
const linkSelection = createLinkRange(lineText, cursorPosition.character, cursorPosition.line, boldSelection ? boldSelection : italicSelection || parent);
let comboSelection: vscode.SelectionRange | undefined;
if (boldSelection && italicSelection && !boldSelection.range.isEqual(italicSelection.range)) {
if (boldSelection.range.contains(italicSelection.range)) {
comboSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, boldSelection);
} else if (italicSelection.range.contains(boldSelection.range)) {
comboSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, italicSelection);
}
}
const linkSelection = createLinkRange(lineText, cursorPosition.character, cursorPosition.line, comboSelection || boldSelection || italicSelection || parent);
const inlineCodeBlockSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, false, linkSelection || parent);
return inlineCodeBlockSelection || linkSelection || boldSelection || italicSelection;
return inlineCodeBlockSelection || linkSelection || comboSelection || boldSelection || italicSelection;
}
function createFencedRange(token: Token, cursorLine: number, document: vscode.TextDocument, parent?: vscode.SelectionRange): vscode.SelectionRange {
@ -154,61 +162,31 @@ function createFencedRange(token: Token, cursorLine: number, document: vscode.Te
}
function createBoldRange(lineText: string, cursorChar: number, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined {
// find closest ** that occurs before cursor position
let startBold = lineText.substring(0, cursorChar).lastIndexOf('**');
// find closest ** that occurs after the start **
const endBoldIndex = lineText.substring(startBold + 2).indexOf('**');
let endBold = startBold + 2 + lineText.substring(startBold + 2).indexOf('**');
if (startBold >= 0 && endBoldIndex >= 0 && startBold + 1 < endBold && startBold <= cursorChar && endBold >= cursorChar) {
const range = new vscode.Range(cursorLine, startBold, cursorLine, endBold + 2);
// **content cursor content** so select content then ** on both sides
const contentRange = new vscode.Range(cursorLine, startBold + 2, cursorLine, endBold);
return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(range, parent));
} else if (startBold >= 0) {
// **content**cursor or **content*cursor*
// find end ** from end of start ** to end of line (since the cursor is within the end stars)
let adjustedEnd = startBold + 2 + lineText.substring(startBold + 2).indexOf('**');
startBold = lineText.substring(0, adjustedEnd - 2).lastIndexOf('**');
if (adjustedEnd >= 0 && cursorChar === adjustedEnd || cursorChar === adjustedEnd + 1) {
if (lineText.charAt(adjustedEnd + 1) === '*') {
// *cursor* so need to extend end to include the second *
adjustedEnd += 1;
}
return new vscode.SelectionRange(new vscode.Range(cursorLine, startBold, cursorLine, adjustedEnd + 1), parent);
}
} else if (endBold > 0) {
// cursor**content** or *cursor*content**
// find start ** from start of string to cursor + 2 (since the cursor is within the start stars)
const adjustedStart = lineText.substring(0, cursorChar + 2).lastIndexOf('**');
endBold = adjustedStart + 2 + lineText.substring(adjustedStart + 2).indexOf('**');
if (adjustedStart >= 0 && adjustedStart === cursorChar || adjustedStart === cursorChar - 1) {
return new vscode.SelectionRange(new vscode.Range(cursorLine, adjustedStart, cursorLine, endBold + 2), parent);
}
const regex = /(?:^|(?<=\s))(?:\*\*\s*([^*]+)(?:\*\s*([^*]+)\s*?\*)*([^*]+)\s*?\*\*)/g;
const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar);
if (matches.length > 0) {
// should only be one match, so select first and index 0 contains the entire match
const bold = matches[0][0];
const startIndex = lineText.indexOf(bold);
const cursorOnStars = cursorChar === startIndex || cursorChar === startIndex + 1 || cursorChar === startIndex + bold.length || cursorChar === startIndex + bold.length - 1;
const contentAndStars = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex, cursorLine, startIndex + bold.length), parent);
const content = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex + 2, cursorLine, startIndex + bold.length - 2), contentAndStars);
return cursorOnStars ? contentAndStars : content;
}
return undefined;
}
function createOtherInlineRange(lineText: string, cursorChar: number, cursorLine: number, isItalic: boolean, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined {
const type = isItalic ? '*' : '`';
const start = lineText.substring(0, cursorChar + 1).lastIndexOf(type);
let end = lineText.substring(cursorChar).indexOf(type);
if (start >= 0 && end >= 0) {
end += cursorChar;
// ensure there's no * or ` before end
const intermediate = lineText.substring(start + 1, end - 1).indexOf(type);
if (intermediate < 0) {
const range = new vscode.Range(cursorLine, start, cursorLine, end + 1);
if (cursorChar > start && cursorChar <= end) {
// within the content so select content then include the stars or backticks
const contentRange = new vscode.Range(cursorLine, start + 1, cursorLine, end);
return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(range, parent));
} else if (cursorChar === start) {
return new vscode.SelectionRange(range, parent);
}
}
const regex = isItalic ? /(?:^|(?<=\s))(?:\*\s*([^*]+)(?:\*\*\s*([^*]+)\s*?\*\*)*([^*]+)\s*?\*)/g : /\`[^\`]*\`/g;
const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar);
if (matches.length > 0) {
// should only be one match, so select first and index 0 contains the entire match
const match = matches[0][0];
const startIndex = lineText.indexOf(match);
const cursorOnType = cursorChar === startIndex || cursorChar === startIndex + match.length;
const contentAndType = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex, cursorLine, startIndex + match.length), parent);
const content = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex + 1, cursorLine, startIndex + match.length - 1), contentAndType);
return cursorOnType ? contentAndType : content;
}
return undefined;
}
@ -228,11 +206,12 @@ function createLinkRange(lineText: string, cursorChar: number, cursorLine: numbe
// determine if cursor is within [text] or (url) in order to know which should be selected
const nearestType = cursorChar >= lineText.indexOf(linkText) && cursorChar < lineText.indexOf(linkText) + linkText.length ? linkText : url;
const indexOfType = lineText.indexOf(nearestType);
// determine if cursor is on a bracket or paren and if so, return the [content] or (content), skipping over the content range
const cursorOnType = cursorChar === lineText.indexOf(nearestType) || cursorChar === lineText.indexOf(nearestType) + nearestType.length;
const cursorOnType = cursorChar === indexOfType || cursorChar === indexOfType + nearestType.length;
const contentAndNearestType = new vscode.SelectionRange(new vscode.Range(cursorLine, lineText.indexOf(nearestType), cursorLine, lineText.indexOf(nearestType) + nearestType.length), linkRange);
const content = new vscode.SelectionRange(new vscode.Range(cursorLine, lineText.indexOf(nearestType) + 1, cursorLine, lineText.indexOf(nearestType) + nearestType.length - 1), contentAndNearestType);
const contentAndNearestType = new vscode.SelectionRange(new vscode.Range(cursorLine, indexOfType, cursorLine, indexOfType + nearestType.length), linkRange);
const content = new vscode.SelectionRange(new vscode.Range(cursorLine, indexOfType + 1, cursorLine, indexOfType + nearestType.length - 1), contentAndNearestType);
return cursorOnType ? contentAndNearestType : content;
}
return undefined;

View file

@ -520,10 +520,10 @@ suite('markdown.SmartSelect', () => {
`paragraph`,
`## sub header`,
`- list`,
`- stuff here [text]**${CURSOR}items in here** and **here**`,
`- stuff here [text] **${CURSOR}items in here** and **here**`,
`- list`
));
assertNestedRangesEqual(ranges![0], [6, 21, 6, 44], [6, 19, 6, 46], [6, 0, 6, 59], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]);
assertNestedRangesEqual(ranges![0], [6, 22, 6, 45], [6, 20, 6, 47], [6, 0, 6, 60], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]);
});
test('Smart select link in paragraph with multiple links', async () => {
const ranges = await getSelectionRangesForDocument(
@ -567,6 +567,27 @@ suite('markdown.SmartSelect', () => {
));
assertNestedRangesEqual(ranges![0], [0, 2, 0, 21], [0, 1, 0, 22], [0, 1, 0, 42], [0, 1, 0, 42], [0, 0, 0, 43], [0, 0, 0, 43]);
});
test('Smart select italic on end', async () => {
const ranges = await getSelectionRangesForDocument(
joinLines(
`*word1 word2 word3${CURSOR}*`
));
assertNestedRangesEqual(ranges![0], [0, 1, 0, 28], [0, 0, 0, 29], [0, 0, 0, 29]);
});
test('Smart select italic then bold', async () => {
const ranges = await getSelectionRangesForDocument(
joinLines(
`outer text **bold words *italic ${CURSOR} words* bold words** outer text`
));
assertNestedRangesEqual(ranges![0], [0, 25, 0, 48], [0, 24, 0, 49], [0, 13, 0, 60], [0, 11, 0, 62], [0, 0, 0, 73]);
});
test('Smart select bold then italic', async () => {
const ranges = await getSelectionRangesForDocument(
joinLines(
`outer text *italic words **bold ${CURSOR} words** italic words* outer text`
));
assertNestedRangesEqual(ranges![0], [0, 27, 0, 48], [0, 25, 0, 50], [0, 12, 0, 63], [0, 11, 0, 64], [0, 0, 0, 75]);
});
});
function assertNestedLineNumbersEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) {