Skip to content

Commit ff31a28

Browse files
authored
Merge pull request #208 from mjbvz/smart-select-link-imrpvoements
Fix smart selection for links in check box list items
2 parents 253e1ae + 33b39d3 commit ff31a28

File tree

12 files changed

+128
-80
lines changed

12 files changed

+128
-80
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 0.5.0-alpha.10 — Unreleased
44
- Improve handling of encoded path completions.
5+
- Improve reliability of smart selection for links and tweak behavior.
56

67
## 0.5.0-alpha.9 — March 24, 2025
78
- Improved detection of multi-line links.

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,11 @@ export function createLanguageService(init: LanguageServiceInitialization): IMdL
256256
const logger = init.logger;
257257

258258
const tocProvider = new MdTableOfContentsProvider(init.parser, init.workspace, logger);
259-
const smartSelectProvider = new MdSelectionRangeProvider(init.parser, tocProvider, logger);
260259
const foldingProvider = new MdFoldingProvider(init.parser, tocProvider, logger);
261260
const linkProvider = new MdLinkProvider(config, init.parser, init.workspace, tocProvider, logger);
262261
const pathCompletionProvider = new MdPathCompletionProvider(config, init.workspace, init.parser, linkProvider, tocProvider);
263262
const linkCache = createWorkspaceLinkCache(init.parser, init.workspace);
263+
const smartSelectProvider = new MdSelectionRangeProvider(init.parser, tocProvider, linkProvider, logger);
264264
const referencesProvider = new MdReferencesProvider(config, init.parser, init.workspace, tocProvider, linkCache, logger);
265265
const definitionsProvider = new MdDefinitionProvider(config, init.workspace, tocProvider, linkCache);
266266
const renameProvider = new MdRenameProvider(config, init.workspace, init.parser, referencesProvider, tocProvider, init.parser.slugifier, logger);

src/languageFeatures/diagnostics.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,15 +379,15 @@ export class DiagnosticComputer {
379379

380380
if (!resolvedHrefPath) {
381381
for (const link of links) {
382-
if (!this.#isIgnoredLink(options, link.source.pathText)) {
382+
if (!this.#isIgnoredLink(options, link.source.hrefPathText)) {
383383
diagnostics.push({
384384
code: DiagnosticCode.link_noSuchFile,
385385
message: l10n.t('File does not exist at path: {0}', path.fsPath),
386386
range: link.source.hrefRange,
387387
severity: pathErrorSeverity,
388388
data: {
389389
fsPath: path.fsPath,
390-
hrefText: link.source.pathText,
390+
hrefText: link.source.hrefPathText,
391391
}
392392
});
393393
}
@@ -407,8 +407,8 @@ export class DiagnosticComputer {
407407
continue;
408408
}
409409

410-
if (!(toc && tocLookupByLink(toc, link)) && !this.#isIgnoredLink(options, link.source.pathText) && !this.#isIgnoredLink(options, link.source.hrefText)) {
411-
const range = (link.source.fragmentRange && modifyRange(link.source.fragmentRange, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 }), undefined)) ?? link.source.hrefRange;
410+
if (!(toc && tocLookupByLink(toc, link)) && !this.#isIgnoredLink(options, link.source.hrefPathText) && !this.#isIgnoredLink(options, link.source.hrefText)) {
411+
const range = (link.source.hrefFragmentRange && modifyRange(link.source.hrefFragmentRange, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 }), undefined)) ?? link.source.hrefRange;
412412
diagnostics.push({
413413
code: DiagnosticCode.link_noSuchHeaderInFile,
414414
message: l10n.t('Header does not exist in file: {0}', link.fragment),

src/languageFeatures/documentHighlights.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ export class MdDocumentHighlightProvider {
5858
for (const link of links) {
5959
if (link.href.kind === HrefKind.Internal
6060
&& toc.lookupByFragment(link.href.fragment) === header
61-
&& link.source.fragmentRange
61+
&& link.source.hrefFragmentRange
6262
&& isSameResource(link.href.path, docUri)
6363
) {
6464
yield {
65-
range: modifyRange(link.source.fragmentRange, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 })),
65+
range: modifyRange(link.source.hrefFragmentRange, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 })),
6666
kind: lsp.DocumentHighlightKind.Read,
6767
};
6868
}
@@ -85,7 +85,7 @@ export class MdDocumentHighlightProvider {
8585
return this.#getHighlightsForReference(link.href.ref, links);
8686
}
8787
case HrefKind.Internal: {
88-
if (link.source.fragmentRange && rangeContains(link.source.fragmentRange, position)) {
88+
if (link.source.hrefFragmentRange && rangeContains(link.source.hrefFragmentRange, position)) {
8989
return this.#getHighlightsForLinkFragment(document, link.href, links, toc);
9090
}
9191

@@ -114,9 +114,9 @@ export class MdDocumentHighlightProvider {
114114

115115
for (const link of links) {
116116
if (link.href.kind === HrefKind.Internal && looksLikePathToResource(this.#configuration, link.href.path, targetDoc)) {
117-
if (link.source.fragmentRange && link.href.fragment.toLowerCase() === fragment) {
117+
if (link.source.hrefFragmentRange && link.href.fragment.toLowerCase() === fragment) {
118118
yield {
119-
range: modifyRange(link.source.fragmentRange, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 })),
119+
range: modifyRange(link.source.hrefFragmentRange, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 })),
120120
kind: lsp.DocumentHighlightKind.Read,
121121
};
122122
}

src/languageFeatures/documentLinks.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ function getFragmentRange(text: string, start: lsp.Position, end: lsp.Position):
111111
return { start: translatePosition(start, { characterDelta: index + 1 }), end };
112112
}
113113

114-
function getLinkSourceFragmentInfo(document: ITextDocument, link: string, linkStart: lsp.Position, linkEnd: lsp.Position): { fragmentRange: lsp.Range | undefined; pathText: string } {
114+
function getLinkSourceFragmentInfo(document: ITextDocument, link: string, linkStart: lsp.Position, linkEnd: lsp.Position): { hrefFragmentRange: lsp.Range | undefined; hrefPathText: string } {
115115
const fragmentRange = getFragmentRange(link, linkStart, linkEnd);
116116
return {
117-
pathText: document.getText({ start: linkStart, end: fragmentRange ? translatePosition(fragmentRange.start, { characterDelta: -1 }) : linkEnd }),
118-
fragmentRange,
117+
hrefPathText: document.getText({ start: linkStart, end: fragmentRange ? translatePosition(fragmentRange.start, { characterDelta: -1 }) : linkEnd }),
118+
hrefFragmentRange: fragmentRange,
119119
};
120120
}
121121

@@ -448,18 +448,20 @@ export class MdLinkComputer {
448448
}
449449

450450
const linkEnd = translatePosition(linkStart, { characterDelta: match[0].length });
451-
const hrefRange = { start: hrefStart, end: hrefEnd };
452451
yield {
453452
kind: MdLinkKind.Link,
454453
source: {
455454
isAngleBracketLink: false,
456455
hrefText: reference,
457-
pathText: reference,
456+
hrefPathText: reference,
458457
resource: getDocUri(document),
459458
range: { start: linkStart, end: linkEnd },
460-
targetRange: hrefRange,
461-
hrefRange: hrefRange,
462-
fragmentRange: undefined,
459+
targetRange: lsp.Range.create(
460+
translatePosition(hrefStart, { characterDelta: -1 }),
461+
translatePosition(hrefEnd, { characterDelta: 1 })
462+
),
463+
hrefRange: lsp.Range.create(hrefStart, hrefEnd),
464+
hrefFragmentRange: undefined,
463465
},
464466
href: {
465467
kind: HrefKind.Reference,

src/languageFeatures/fileRename.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class MdFileRenameProvider {
116116
}
117117

118118
// If the link was within a file in the moved dir but traversed out of it, we also need to update the path
119-
if (link.source.pathText.startsWith('..') && isParentDir(edit.newUri, docUri)) {
119+
if (link.source.hrefPathText.startsWith('..') && isParentDir(edit.newUri, docUri)) {
120120
// Resolve the link relative to the old file path
121121
const oldDocUri = docUri.with({
122122
path: Utils.joinPath(edit.oldUri, path.posix.relative(edit.newUri.path, docUri.path)).path
@@ -136,7 +136,7 @@ export class MdFileRenameProvider {
136136
}
137137

138138
const replacementPath = encodeURI(newPathText);
139-
if (replacementPath !== link.source.pathText) {
139+
if (replacementPath !== link.source.hrefPathText) {
140140
const { range, newText } = getLinkRenameEdit(link, replacementPath);
141141
builder.replace(docUri, range, newText);
142142
didParticipate = true;
@@ -240,7 +240,7 @@ export class MdFileRenameProvider {
240240
}
241241

242242
const newFilePath = removeNewUriExtIfNeeded(this.#config, link.href, newUri);
243-
const newLinkText = getLinkRenameText(this.#workspace, link.source, newFilePath, link.source.pathText.startsWith('.'));
243+
const newLinkText = getLinkRenameText(this.#workspace, link.source, newFilePath, link.source.hrefPathText.startsWith('.'));
244244
if (typeof newLinkText === 'string') {
245245
const { range, newText } = getLinkRenameEdit(link, newLinkText);
246246
builder.replace(doc, range, newText);

src/languageFeatures/references.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ export class MdReferencesProvider extends Disposable {
226226

227227
const references: MdReference[] = [];
228228

229-
if (resolvedResource && this.#isMarkdownPath(resolvedResource) && sourceLink.href.fragment && sourceLink.source.fragmentRange && rangeContains(sourceLink.source.fragmentRange, triggerPosition)) {
229+
if (resolvedResource && this.#isMarkdownPath(resolvedResource) && sourceLink.href.fragment && sourceLink.source.hrefFragmentRange && rangeContains(sourceLink.source.hrefFragmentRange, triggerPosition)) {
230230
const toc = await this.#tocProvider.get(resolvedResource);
231231
const entry = toc?.lookupByFragment(sourceLink.href.fragment);
232232
if (entry) {
@@ -325,8 +325,8 @@ export class MdReferencesProvider extends Disposable {
325325
* Get just the range of the file path, dropping the fragment
326326
*/
327327
#getPathRange(link: MdLink): lsp.Range {
328-
return link.source.fragmentRange
329-
? modifyRange(link.source.hrefRange, undefined, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 }))
328+
return link.source.hrefFragmentRange
329+
? modifyRange(link.source.hrefRange, undefined, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 }))
330330
: link.source.hrefRange;
331331
}
332332
}

src/languageFeatures/rename.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ export class MdRenameProvider {
103103
}
104104

105105
// See if we are renaming the fragment or the path
106-
const { fragmentRange } = triggerRef.link.source;
107-
if (fragmentRange && rangeContains(fragmentRange, position)) {
106+
const { hrefFragmentRange } = triggerRef.link.source;
107+
if (hrefFragmentRange && rangeContains(hrefFragmentRange, position)) {
108108
const declaration = this.#findHeaderDeclaration(allRefsInfo.references);
109109
return {
110-
range: fragmentRange,
111-
placeholder: declaration ? declaration.headerText : document.getText(fragmentRange),
110+
range: hrefFragmentRange,
111+
placeholder: declaration ? declaration.headerText : document.getText(hrefFragmentRange),
112112
};
113113
}
114114

@@ -141,9 +141,9 @@ export class MdRenameProvider {
141141
return this.#renameReferenceLinks(allRefsInfo, newName);
142142
} else if (triggerRef.kind === MdReferenceKind.Link && triggerRef.link.href.kind === HrefKind.External) {
143143
return this.#renameExternalLink(allRefsInfo, newName);
144-
} else if (triggerRef.kind === MdReferenceKind.Header || (triggerRef.kind === MdReferenceKind.Link && triggerRef.link.source.fragmentRange && rangeContains(triggerRef.link.source.fragmentRange, position) && (triggerRef.link.kind === MdLinkKind.Definition || triggerRef.link.kind === MdLinkKind.Link && triggerRef.link.href.kind === HrefKind.Internal))) {
144+
} else if (triggerRef.kind === MdReferenceKind.Header || (triggerRef.kind === MdReferenceKind.Link && triggerRef.link.source.hrefFragmentRange && rangeContains(triggerRef.link.source.hrefFragmentRange, position) && (triggerRef.link.kind === MdLinkKind.Definition || triggerRef.link.kind === MdLinkKind.Link && triggerRef.link.href.kind === HrefKind.Internal))) {
145145
return this.#renameFragment(allRefsInfo, newName, token);
146-
} else if (triggerRef.kind === MdReferenceKind.Link && !(triggerRef.link.source.fragmentRange && rangeContains(triggerRef.link.source.fragmentRange, position)) && (triggerRef.link.kind === MdLinkKind.Link || triggerRef.link.kind === MdLinkKind.Definition) && triggerRef.link.href.kind === HrefKind.Internal) {
146+
} else if (triggerRef.kind === MdReferenceKind.Link && !(triggerRef.link.source.hrefFragmentRange && rangeContains(triggerRef.link.source.hrefFragmentRange, position)) && (triggerRef.link.kind === MdLinkKind.Link || triggerRef.link.kind === MdLinkKind.Definition) && triggerRef.link.href.kind === HrefKind.Internal) {
147147
return this.#renameFilePath(triggerRef.link.source.resource, triggerRef.link.href, allRefsInfo, newName, token);
148148
}
149149

@@ -263,7 +263,7 @@ export class MdRenameProvider {
263263

264264
for (const ref of refs?.references ?? []) {
265265
if (ref.kind === MdReferenceKind.Link) {
266-
builder.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, changedHeader.slug.value);
266+
builder.replace(ref.link.source.resource, ref.link.source.hrefFragmentRange ?? ref.location.range, changedHeader.slug.value);
267267
}
268268
}
269269
}
@@ -277,7 +277,7 @@ export class MdRenameProvider {
277277
break;
278278

279279
case MdReferenceKind.Link:
280-
builder.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, !ref.link.source.fragmentRange || ref.link.href.kind === HrefKind.External ? newHeaderText : newSlug.value);
280+
builder.replace(ref.link.source.resource, ref.link.source.hrefFragmentRange ?? ref.location.range, !ref.link.source.hrefFragmentRange || ref.link.href.kind === HrefKind.External ? newHeaderText : newSlug.value);
281281
break;
282282
}
283283
}
@@ -302,7 +302,7 @@ export class MdRenameProvider {
302302
if (ref.link.kind === MdLinkKind.Definition) {
303303
builder.replace(ref.link.source.resource, ref.link.ref.range, newName);
304304
} else {
305-
builder.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, newName);
305+
builder.replace(ref.link.source.resource, ref.link.source.hrefFragmentRange ?? ref.location.range, newName);
306306
}
307307
}
308308
}
@@ -356,8 +356,8 @@ export function getLinkRenameText(workspace: IWorkspace, source: MdLinkSource, n
356356
}
357357

358358
export function getFilePathRange(link: MdLink): lsp.Range {
359-
if (link.source.fragmentRange) {
360-
return modifyRange(link.source.hrefRange, undefined, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 }));
359+
if (link.source.hrefFragmentRange) {
360+
return modifyRange(link.source.hrefRange, undefined, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 }));
361361
}
362362
return link.source.hrefRange;
363363
}

0 commit comments

Comments
 (0)