Skip to content

Commit

Permalink
Merge pull request #4448 from v4hn/pr-fix-broken-symbol-decoration
Browse files Browse the repository at this point in the history
fix link highlighting in \ref and \input for non-word characters
  • Loading branch information
jlelong authored Nov 4, 2024
2 parents e2f41b7 + a177a16 commit b797c80
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 30 deletions.
65 changes: 51 additions & 14 deletions src/language/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,52 +40,89 @@ export class DefinitionProvider implements vscode.DefinitionProvider {
return
}

async provideDefinition(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location | undefined> {
/**
* VSCode hook to provide definitions of the symbol at `position`.
* In LW these can be custom commands, labels, citations, glossary entries, and file names.
*
* Also provides the exact range of the found symbol (`originSelectionRange`),
* as different symbol types support different characters in LaTeX (esp. regarding `[:-]`)
*
* @param document The document to be scanned.
* @param position The position to be scanned at.
*
* @returns {DefinitionLink[]} linking `originSelectionRange` to `targetUri`/`targetRange`
*/
async provideDefinition(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.DefinitionLink[]> {
if (document.uri.scheme !== 'file') {
return
return []
}
const token = tokenizer(document, position)
if (token === undefined) {
return
const tokenRange = tokenizer(document, position)
if (tokenRange === undefined) {
return []
}
const token = document.getText(tokenRange)

if (token.startsWith('\\')) {
const macro = lw.completion.macro.getData().definedCmds.get(token.slice(1))
if (macro) {
return macro.location
return [{
targetUri: macro.location.uri,
targetRange: macro.location.range,
originSelectionRange: tokenRange
}]
}
return
return []
}
const ref = lw.completion.reference.getItem(token)
if (ref) {
return new vscode.Location(vscode.Uri.file(ref.file), ref.position)
return [{
targetUri: vscode.Uri.file(ref.file),
targetRange: new vscode.Range(ref.position, ref.position),
originSelectionRange: tokenRange
}]
}
const cite = lw.completion.citation.getItem(token)
if (cite) {
return new vscode.Location(vscode.Uri.file(cite.file), cite.position)
return [{
targetUri: vscode.Uri.file(cite.file),
targetRange: new vscode.Range(cite.position, cite.position),
originSelectionRange: tokenRange
}]
}
const glossary = lw.completion.glossary.getItem(token)
if (glossary) {
return new vscode.Location(vscode.Uri.file(glossary.filePath), glossary.position)
return [{
targetUri: vscode.Uri.file(glossary.filePath),
targetRange: new vscode.Range(glossary.position, glossary.position),
originSelectionRange: tokenRange
}]
}
if (vscode.window.activeTextEditor && token.includes('.')) {
// We skip graphics files
const graphicsExtensions = ['.pdf', '.eps', '.jpg', '.jpeg', '.JPG', '.JPEG', '.gif', '.png']
const ext = path.extname(token)
if (graphicsExtensions.includes(ext)) {
return
return []
}
const absolutePath = path.resolve(path.dirname(vscode.window.activeTextEditor.document.fileName), token)
if (fs.existsSync(absolutePath)) {
return new vscode.Location( vscode.Uri.file(absolutePath), new vscode.Position(0, 0) )
return [{
targetUri: vscode.Uri.file(absolutePath),
targetRange: new vscode.Range(0, 0, 0, 0),
originSelectionRange: tokenRange
}]
}
}

const filename = await this.onAFilename(document, position, token)
if (filename) {
return new vscode.Location( vscode.Uri.file(filename), new vscode.Position(0, 0) )
return [{
targetUri: vscode.Uri.file(filename),
targetRange: new vscode.Range(0, 0, 0, 0),
originSelectionRange: tokenRange
}]
}
return
return []
}

}
2 changes: 1 addition & 1 deletion src/preview/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class HoverProvider implements vscode.HoverProvider {
return graphicsHover
}
}
const token = tokenizer(document, position)
const token = document.getText(tokenizer(document, position))
if (!token) {
return
}
Expand Down
30 changes: 15 additions & 15 deletions src/utils/tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import * as vscode from 'vscode'
import * as utils from './utils'

/**
* If a string on `position` is like `\macro`, `\macro{` or `\macro[`,
* return `\macro`.
* If the string at `position` is a latex command, e.g., `\macro`, `\macro{` or `\macro[`,
* return the range of the command string (`\macro`).
*
* @param document The document to be scanned.
* @param position The position to be scanned at.
*/
function macroTokenizer(document: vscode.TextDocument, position: vscode.Position): string | undefined {
function macroTokenizer(document: vscode.TextDocument, position: vscode.Position): vscode.Range | undefined {
let startRegex: RegExp
if (document.languageId === 'latex-expl3') {
startRegex = /\\(?=[^\\{},[\]]*$)/
Expand All @@ -21,26 +21,26 @@ function macroTokenizer(document: vscode.TextDocument, position: vscode.Position
}
const firstBracket = document.getText(new vscode.Range(position, new vscode.Position(position.line, 65535))).match(/[[{]/)
if (firstBracket && firstBracket.index !== undefined && firstBracket.index > 0) {
return document.getText(new vscode.Range(
return new vscode.Range(
new vscode.Position(position.line, startResult.index),
new vscode.Position(position.line, position.character + firstBracket.index)
)).trim()
)
}
const wordRange = document.getWordRangeAtPosition(position)
if (wordRange) {
return document.getText(wordRange.with(new vscode.Position(position.line, startResult.index))).trim()
return wordRange.with(new vscode.Position(position.line, startResult.index))
}
return
}

/**
* If a string on `position` surround by `{...}` or `[...]`,
* return the string inside brackets.
* If the string at `position` is surround by `{...}` or `[...]`,
* return the range for the argument at `position` inside the brackets.
*
* @param document The document to be scanned.
* @param position The position to be scanned at.
*/
function argTokenizer(document: vscode.TextDocument, position: vscode.Position): string | undefined {
function argTokenizer(document: vscode.TextDocument, position: vscode.Position): vscode.Range | undefined {
const startResult = document.getText(new vscode.Range(new vscode.Position(position.line, 0), position)).match(/[{,[](?=[^{},[\]]*$)/)
if (startResult === null || startResult.index === undefined || startResult.index < 0) {
return
Expand All @@ -49,22 +49,22 @@ function argTokenizer(document: vscode.TextDocument, position: vscode.Position):
if (endResult === null || endResult.index === undefined || endResult.index < 0) {
return
}
return document.getText(new vscode.Range(
return new vscode.Range(
new vscode.Position(position.line, startResult.index + 1),
new vscode.Position(position.line, position.character + endResult.index)
)).trim()
)
}


/**
* If a string on `position` is like `\macro{` or `\macro[`, then
* returns the `\macro`. If it is like `{...}` or `[...]`, then
* returns the string inside brackets.
* If the string at `position` is like `\macro{` or `\macro[`, then
* returns the range for `\macro`. If it is like `{...}` or `[...]`, then
* returns the range the argument inside brackets.
*
* @param document The document to be scanned.
* @param position The position to be scanned at.
*/
export function tokenizer(document: vscode.TextDocument, position: vscode.Position): string | undefined {
export function tokenizer(document: vscode.TextDocument, position: vscode.Position): vscode.Range | undefined {
// \macro case
const macroToken = macroTokenizer(document, position)
if (macroToken) {
Expand Down

0 comments on commit b797c80

Please sign in to comment.