Skip to content

Commit

Permalink
Store ref math on parsing, partial #4156
Browse files Browse the repository at this point in the history
  • Loading branch information
James-Yu committed Feb 16, 2024
1 parent e161c10 commit dd74beb
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 165 deletions.
75 changes: 41 additions & 34 deletions src/completion/completer/reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as fs from 'fs'
import * as path from 'path'
import type * as Ast from '@unified-latex/unified-latex-types'
import { lw } from '../../lw'
import type { CompletionArgs, CompletionItem, CompletionProvider, FileCache, ReferenceDocType, ReferenceEntry } from '../../types'
import type { CompletionArgs, CompletionProvider, FileCache, ReferenceItem, TeXMathEnv } from '../../types'
import { getLongestBalancedString, stripEnvironments } from '../../utils/utils'
import { computeFilteringRange } from './completerutils'
import { argContentToStr } from '../../utils/parser'
Expand All @@ -16,7 +16,7 @@ export const reference = {
}

const data = {
suggestions: new Map<string, ReferenceEntry>(),
suggestions: new Map<string, ReferenceItem>(),
prevIndexObj: new Map<string, {refNumber: string, pageNumber: string}>()
}

Expand All @@ -31,29 +31,12 @@ function provide(line: string, position: vscode.Position): vscode.CompletionItem
keys = Array.from(new Set(keys))
const items: vscode.CompletionItem[] = []
for (const key of keys) {
const suggestion = data.suggestions.get(key)
if (suggestion) {
const refDoc: ReferenceDocType = {
documentation: suggestion.documentation,
file: suggestion.file,
position: {
line: suggestion.position.line,
character: suggestion.position.character
},
key,
label: suggestion.label,
prevIndex: suggestion.prevIndex
}
suggestion.documentation = JSON.stringify(refDoc)
items.push(suggestion)
} else {
items.push({label: key})
}
items.push(data.suggestions.get(key) ?? {label: key})
}
return items
}

function getItem(token: string): ReferenceEntry | undefined {
function getItem(token: string): ReferenceItem | undefined {
updateAll()
return data.suggestions.get(token)
}
Expand Down Expand Up @@ -123,8 +106,6 @@ function updateAll(line?: string, position?: vscode.Position) {
const label = (cachedFile in prefixes ? prefixes[cachedFile] : '') + ref.label
data.suggestions.set(label, {...ref,
label,
file: cachedFile,
position: 'inserting' in ref.range ? ref.range.inserting.start : ref.range.start,
range,
prevIndex: data.prevIndexObj.get(label)
})
Expand All @@ -143,14 +124,14 @@ function parse(cache: FileCache) {
if (cache.ast !== undefined) {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const labelMacros = configuration.get('intellisense.label.command') as string[]
cache.elements.reference = parseAst(cache.ast, cache.content.split('\n'), labelMacros)
cache.elements.reference = parseAst(cache.ast, [], cache.filePath, cache.content.split('\n'), labelMacros)
} else {
cache.elements.reference = parseContent(cache.content)
cache.elements.reference = parseContent(cache.content, cache.filePath)
}
}

function parseAst(node: Ast.Node, lines: string[], labelMacros: string[]): CompletionItem[] {
let refs: CompletionItem[] = []
function parseAst(node: Ast.Node, nodeStack: Ast.Node[], filePath: string, lines: string[], labelMacros: string[]): ReferenceItem[] {
let refs: ReferenceItem[] = []
if (node.type === 'macro' &&
['renewcommand', 'newcommand', 'providecommand', 'DeclareMathOperator', 'renewenvironment', 'newenvironment'].includes(node.content)) {
// Do not scan labels inside \newcommand, \newenvironment & co
Expand Down Expand Up @@ -179,20 +160,24 @@ function parseAst(node: Ast.Node, lines: string[], labelMacros: string[]): Compl
}

if (label !== '' && node.position !== undefined) {
refs.push({
const ref: ReferenceItem = {
label,
kind: vscode.CompletionItemKind.Reference,
// One row before, four rows after
documentation: lines.slice(node.position.start.line - 2, node.position.end.line + 4).join('\n'),
// Here we abuse the definition of range to store the location of the reference definition
range: new vscode.Range(node.position.start.line - 1, node.position.start.column - 1,
node.position.end.line - 1, node.position.end.column - 1)
})
node.position.end.line - 1, node.position.end.column - 1),
file: filePath,
position: new vscode.Position(node.position.start.line - 1, node.position.start.column - 1),
math: findMath(nodeStack)
}
refs.push(ref)
}

const parseContentNodes = (content: Ast.Node[]) => {
for (const subNode of content) {
refs = [...refs, ...parseAst(subNode, lines, labelMacros)]
refs = [...refs, ...parseAst(subNode, [...nodeStack, node], filePath, lines, labelMacros)]
}
}
if (node.type === 'macro' && node.args) {
Expand All @@ -206,9 +191,29 @@ function parseAst(node: Ast.Node, lines: string[], labelMacros: string[]): Compl
return refs
}

function parseContent(content: string): CompletionItem[] {
function findMath(nodeStack: Ast.Node[]): TeXMathEnv | undefined {
const node = nodeStack[nodeStack.length - 1]
if (node.type !== 'environment' && node.type !== 'mathenv') {
return
}
const env = (typeof node.env === 'string') ? node.env : (node.env as unknown as {content: string}).content
if (![
'align', 'align\\*', 'alignat', 'alignat\\*', 'aligned', 'alignedat', 'array', 'Bmatrix', 'bmatrix', 'cases', 'CD', 'eqnarray', 'eqnarray\\*', 'equation', 'equation\\*', 'flalign', 'flalign\\*', 'gather', 'gather\\*', 'gathered', 'matrix', 'multline', 'multline\\*', 'pmatrix', 'smallmatrix', 'split', 'subarray', 'Vmatrix', 'vmatrix'
].includes(env)) {
return
}
const math = lw.parse.stringify(node)
return {
envname: env,
range: new vscode.Range((node.position?.start.line ?? 1) - 1, (node.position?.start.column ?? 1) - 1,
(node.position?.end.line ?? 1) - 1, (node.position?.end.column ?? 1) - 1),
texString: math
}
}

function parseContent(content: string, filePath: string): ReferenceItem[] {
const refReg = /(?:\\label(?:\[[^[\]{}]*\])?|(?:^|[,\s])label=){([^#\\}]*)}/gm
const refs: CompletionItem[] = []
const refs: ReferenceItem[] = []
const refList: string[] = []
content = stripEnvironments(content, [''])
while (true) {
Expand All @@ -230,7 +235,9 @@ function parseContent(content: string): CompletionItem[] {
documentation: content.substring(prevContent.lastIndexOf('\n') + 1, result.index + followLength),
// Here we abuse the definition of range to store the location of the reference definition
range: new vscode.Range(positionContent.length - 1, positionContent[positionContent.length - 1].length,
positionContent.length - 1, positionContent[positionContent.length - 1].length)
positionContent.length - 1, positionContent[positionContent.length - 1].length),
file: filePath,
position: new vscode.Position(positionContent.length - 1, positionContent[positionContent.length - 1].length)
})
refList.push(result[1])
}
Expand Down
27 changes: 8 additions & 19 deletions src/completion/latex.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from 'vscode'
import { lw } from '../lw'
import type { CompletionArgs, CompletionProvider, ReferenceDocType } from '../types'
import type { CompletionArgs, CompletionProvider, ReferenceItem } from '../types'
import { citation, provider as citationProvider } from './completer/citation'
import { provider as environmentProvider } from './completer/environment'
import { provider as macroProvider } from './completer/macro'
Expand Down Expand Up @@ -53,30 +53,19 @@ export class Provider implements vscode.CompletionItemProvider {
return []
}

async resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken): Promise<vscode.CompletionItem> {
async resolveCompletionItem(item: vscode.CompletionItem, ctoken: vscode.CancellationToken): Promise<vscode.CompletionItem> {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
if (item.kind === vscode.CompletionItemKind.Reference) {
if (typeof item.documentation !== 'string') {
if (!('file' in item) || !configuration.get('hover.ref.enabled')) {
return item
}
const data = JSON.parse(item.documentation) as ReferenceDocType
const sug = {
file: data.file,
position: new vscode.Position(data.position.line, data.position.character)
}
if (!configuration.get('hover.ref.enabled')) {
item.documentation = data.documentation
return item
}
const tex = lw.preview.math.findRef(sug, data.key)
if (tex) {
const svgDataUrl = await lw.preview.math.renderSvgOnRef(tex, data, token)
item.documentation = new vscode.MarkdownString(`![equation](${svgDataUrl})`)
return item
} else {
item.documentation = data.documentation
const refItem = item as ReferenceItem
if (!refItem.math) {
return item
}
const svgDataUrl = await lw.preview.math.ref2svg(refItem, ctoken)
item.documentation = new vscode.MarkdownString(`![equation](${svgDataUrl})`)
return item
} else if (item.kind === vscode.CompletionItemKind.File) {
const preview = configuration.get('intellisense.includegraphics.preview.enabled') as boolean
if (!preview) {
Expand Down
2 changes: 1 addition & 1 deletion src/extras/math-preview-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function open() {
}
return
}
lw.preview.math.getColor()
lw.preview.math.refreshMathColor()
const panel = vscode.window.createWebviewPanel(
'latex-workshop-mathpreview',
'Math Preview',
Expand Down
6 changes: 4 additions & 2 deletions src/preview/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ export {

class HoverProvider implements vscode.HoverProvider {
public async provideHover(document: vscode.TextDocument, position: vscode.Position, ctoken: vscode.CancellationToken): Promise<vscode.Hover | undefined> {
lw.preview.math.getColor()
lw.preview.math.refreshMathColor()
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const hov = configuration.get('hover.preview.enabled') as boolean
const hovReference = configuration.get('hover.ref.enabled') as boolean
const hovCitation = configuration.get('hover.citation.enabled') as boolean
const hovCommand = configuration.get('hover.command.enabled') as boolean
if (hov) {
const tex = lw.preview.math.findTeX(document, position)
// Hovered over equations
if (tex) {
const hover = await lw.preview.math.onTeX(document, tex, await findMacros(ctoken))
return hover
}
// Hovered over graphics
const graphicsHover = await lw.preview.hover(document, position)
if (graphicsHover) {
return graphicsHover
Expand Down Expand Up @@ -49,7 +51,7 @@ class HoverProvider implements vscode.HoverProvider {
}
const refData = lw.completion.reference.getItem(token)
if (hovReference && refData) {
const hover = await lw.preview.math.onRef(document, position, refData, token, ctoken)
const hover = await lw.preview.math.onRef(document, position, refData, ctoken)
return hover
}
const cite = lw.completion.citation.getItem(token, document.uri)
Expand Down
Loading

0 comments on commit dd74beb

Please sign in to comment.