Skip to content

Fix smart selection for links in check box list items #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

## 0.5.0-alpha.9 — March 24, 2025
- Improved detection of multi-line links.
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,11 @@ export function createLanguageService(init: LanguageServiceInitialization): IMdL
const logger = init.logger;

const tocProvider = new MdTableOfContentsProvider(init.parser, init.workspace, logger);
const smartSelectProvider = new MdSelectionRangeProvider(init.parser, tocProvider, logger);
const foldingProvider = new MdFoldingProvider(init.parser, tocProvider, logger);
const linkProvider = new MdLinkProvider(config, init.parser, init.workspace, tocProvider, logger);
const pathCompletionProvider = new MdPathCompletionProvider(config, init.workspace, init.parser, linkProvider, tocProvider);
const linkCache = createWorkspaceLinkCache(init.parser, init.workspace);
const smartSelectProvider = new MdSelectionRangeProvider(init.parser, tocProvider, linkProvider, logger);
const referencesProvider = new MdReferencesProvider(config, init.parser, init.workspace, tocProvider, linkCache, logger);
const definitionsProvider = new MdDefinitionProvider(config, init.workspace, tocProvider, linkCache);
const renameProvider = new MdRenameProvider(config, init.workspace, init.parser, referencesProvider, tocProvider, init.parser.slugifier, logger);
Expand Down
8 changes: 4 additions & 4 deletions src/languageFeatures/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,15 +379,15 @@ export class DiagnosticComputer {

if (!resolvedHrefPath) {
for (const link of links) {
if (!this.#isIgnoredLink(options, link.source.pathText)) {
if (!this.#isIgnoredLink(options, link.source.hrefPathText)) {
diagnostics.push({
code: DiagnosticCode.link_noSuchFile,
message: l10n.t('File does not exist at path: {0}', path.fsPath),
range: link.source.hrefRange,
severity: pathErrorSeverity,
data: {
fsPath: path.fsPath,
hrefText: link.source.pathText,
hrefText: link.source.hrefPathText,
}
});
}
Expand All @@ -407,8 +407,8 @@ export class DiagnosticComputer {
continue;
}

if (!(toc && tocLookupByLink(toc, link)) && !this.#isIgnoredLink(options, link.source.pathText) && !this.#isIgnoredLink(options, link.source.hrefText)) {
const range = (link.source.fragmentRange && modifyRange(link.source.fragmentRange, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 }), undefined)) ?? link.source.hrefRange;
if (!(toc && tocLookupByLink(toc, link)) && !this.#isIgnoredLink(options, link.source.hrefPathText) && !this.#isIgnoredLink(options, link.source.hrefText)) {
const range = (link.source.hrefFragmentRange && modifyRange(link.source.hrefFragmentRange, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 }), undefined)) ?? link.source.hrefRange;
diagnostics.push({
code: DiagnosticCode.link_noSuchHeaderInFile,
message: l10n.t('Header does not exist in file: {0}', link.fragment),
Expand Down
10 changes: 5 additions & 5 deletions src/languageFeatures/documentHighlights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ export class MdDocumentHighlightProvider {
for (const link of links) {
if (link.href.kind === HrefKind.Internal
&& toc.lookupByFragment(link.href.fragment) === header
&& link.source.fragmentRange
&& link.source.hrefFragmentRange
&& isSameResource(link.href.path, docUri)
) {
yield {
range: modifyRange(link.source.fragmentRange, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 })),
range: modifyRange(link.source.hrefFragmentRange, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 })),
kind: lsp.DocumentHighlightKind.Read,
};
}
Expand All @@ -85,7 +85,7 @@ export class MdDocumentHighlightProvider {
return this.#getHighlightsForReference(link.href.ref, links);
}
case HrefKind.Internal: {
if (link.source.fragmentRange && rangeContains(link.source.fragmentRange, position)) {
if (link.source.hrefFragmentRange && rangeContains(link.source.hrefFragmentRange, position)) {
return this.#getHighlightsForLinkFragment(document, link.href, links, toc);
}

Expand Down Expand Up @@ -114,9 +114,9 @@ export class MdDocumentHighlightProvider {

for (const link of links) {
if (link.href.kind === HrefKind.Internal && looksLikePathToResource(this.#configuration, link.href.path, targetDoc)) {
if (link.source.fragmentRange && link.href.fragment.toLowerCase() === fragment) {
if (link.source.hrefFragmentRange && link.href.fragment.toLowerCase() === fragment) {
yield {
range: modifyRange(link.source.fragmentRange, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 })),
range: modifyRange(link.source.hrefFragmentRange, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 })),
kind: lsp.DocumentHighlightKind.Read,
};
}
Expand Down
18 changes: 10 additions & 8 deletions src/languageFeatures/documentLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ function getFragmentRange(text: string, start: lsp.Position, end: lsp.Position):
return { start: translatePosition(start, { characterDelta: index + 1 }), end };
}

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

Expand Down Expand Up @@ -448,18 +448,20 @@ export class MdLinkComputer {
}

const linkEnd = translatePosition(linkStart, { characterDelta: match[0].length });
const hrefRange = { start: hrefStart, end: hrefEnd };
yield {
kind: MdLinkKind.Link,
source: {
isAngleBracketLink: false,
hrefText: reference,
pathText: reference,
hrefPathText: reference,
resource: getDocUri(document),
range: { start: linkStart, end: linkEnd },
targetRange: hrefRange,
hrefRange: hrefRange,
fragmentRange: undefined,
targetRange: lsp.Range.create(
translatePosition(hrefStart, { characterDelta: -1 }),
translatePosition(hrefEnd, { characterDelta: 1 })
),
hrefRange: lsp.Range.create(hrefStart, hrefEnd),
hrefFragmentRange: undefined,
},
href: {
kind: HrefKind.Reference,
Expand Down
6 changes: 3 additions & 3 deletions src/languageFeatures/fileRename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class MdFileRenameProvider {
}

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

const replacementPath = encodeURI(newPathText);
if (replacementPath !== link.source.pathText) {
if (replacementPath !== link.source.hrefPathText) {
const { range, newText } = getLinkRenameEdit(link, replacementPath);
builder.replace(docUri, range, newText);
didParticipate = true;
Expand Down Expand Up @@ -240,7 +240,7 @@ export class MdFileRenameProvider {
}

const newFilePath = removeNewUriExtIfNeeded(this.#config, link.href, newUri);
const newLinkText = getLinkRenameText(this.#workspace, link.source, newFilePath, link.source.pathText.startsWith('.'));
const newLinkText = getLinkRenameText(this.#workspace, link.source, newFilePath, link.source.hrefPathText.startsWith('.'));
if (typeof newLinkText === 'string') {
const { range, newText } = getLinkRenameEdit(link, newLinkText);
builder.replace(doc, range, newText);
Expand Down
6 changes: 3 additions & 3 deletions src/languageFeatures/references.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export class MdReferencesProvider extends Disposable {

const references: MdReference[] = [];

if (resolvedResource && this.#isMarkdownPath(resolvedResource) && sourceLink.href.fragment && sourceLink.source.fragmentRange && rangeContains(sourceLink.source.fragmentRange, triggerPosition)) {
if (resolvedResource && this.#isMarkdownPath(resolvedResource) && sourceLink.href.fragment && sourceLink.source.hrefFragmentRange && rangeContains(sourceLink.source.hrefFragmentRange, triggerPosition)) {
const toc = await this.#tocProvider.get(resolvedResource);
const entry = toc?.lookupByFragment(sourceLink.href.fragment);
if (entry) {
Expand Down Expand Up @@ -325,8 +325,8 @@ export class MdReferencesProvider extends Disposable {
* Get just the range of the file path, dropping the fragment
*/
#getPathRange(link: MdLink): lsp.Range {
return link.source.fragmentRange
? modifyRange(link.source.hrefRange, undefined, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 }))
return link.source.hrefFragmentRange
? modifyRange(link.source.hrefRange, undefined, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 }))
: link.source.hrefRange;
}
}
22 changes: 11 additions & 11 deletions src/languageFeatures/rename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ export class MdRenameProvider {
}

// See if we are renaming the fragment or the path
const { fragmentRange } = triggerRef.link.source;
if (fragmentRange && rangeContains(fragmentRange, position)) {
const { hrefFragmentRange } = triggerRef.link.source;
if (hrefFragmentRange && rangeContains(hrefFragmentRange, position)) {
const declaration = this.#findHeaderDeclaration(allRefsInfo.references);
return {
range: fragmentRange,
placeholder: declaration ? declaration.headerText : document.getText(fragmentRange),
range: hrefFragmentRange,
placeholder: declaration ? declaration.headerText : document.getText(hrefFragmentRange),
};
}

Expand Down Expand Up @@ -141,9 +141,9 @@ export class MdRenameProvider {
return this.#renameReferenceLinks(allRefsInfo, newName);
} else if (triggerRef.kind === MdReferenceKind.Link && triggerRef.link.href.kind === HrefKind.External) {
return this.#renameExternalLink(allRefsInfo, newName);
} 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))) {
} 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))) {
return this.#renameFragment(allRefsInfo, newName, token);
} 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) {
} 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) {
return this.#renameFilePath(triggerRef.link.source.resource, triggerRef.link.href, allRefsInfo, newName, token);
}

Expand Down Expand Up @@ -263,7 +263,7 @@ export class MdRenameProvider {

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

case MdReferenceKind.Link:
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);
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);
break;
}
}
Expand All @@ -302,7 +302,7 @@ export class MdRenameProvider {
if (ref.link.kind === MdLinkKind.Definition) {
builder.replace(ref.link.source.resource, ref.link.ref.range, newName);
} else {
builder.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, newName);
builder.replace(ref.link.source.resource, ref.link.source.hrefFragmentRange ?? ref.location.range, newName);
}
}
}
Expand Down Expand Up @@ -356,8 +356,8 @@ export function getLinkRenameText(workspace: IWorkspace, source: MdLinkSource, n
}

export function getFilePathRange(link: MdLink): lsp.Range {
if (link.source.fragmentRange) {
return modifyRange(link.source.hrefRange, undefined, translatePosition(link.source.fragmentRange.start, { characterDelta: -1 }));
if (link.source.hrefFragmentRange) {
return modifyRange(link.source.hrefRange, undefined, translatePosition(link.source.hrefFragmentRange.start, { characterDelta: -1 }));
}
return link.source.hrefRange;
}
Expand Down
Loading