Skip to content

Commit

Permalink
Env suggestion should insert env name when in {} pair
Browse files Browse the repository at this point in the history
  • Loading branch information
James-Yu committed Oct 3, 2024
1 parent e27630d commit 4c1e1b4
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 26 deletions.
96 changes: 70 additions & 26 deletions src/completion/completer/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import * as fs from 'fs'
import type * as Ast from '@unified-latex/unified-latex-types'
import { lw } from '../../lw'
import { EnvSnippetType } from '../../types'
import type { CompletionArgs, CompletionItem, CompletionProvider, EnvironmentInfo, EnvironmentRaw, FileCache } from '../../types'
import type {
CompletionArgs,
CompletionItem,
CompletionProvider,
EnvironmentInfo,
EnvironmentRaw,
FileCache,
} from '../../types'
import { CmdEnvSuggestion, filterNonLetterSuggestions, filterArgumentHint } from './completerutils'

export const provider: CompletionProvider = { from }
Expand All @@ -12,7 +19,7 @@ export const environment = {
getDefaultEnvs,
setPackageEnvs,
getEnvFromPkg,
provideEnvsAsMacroInPkg
provideEnvsAsMacroInPkg,
}

const data = {
Expand All @@ -22,18 +29,26 @@ const data = {
packageEnvs: new Map<string, EnvironmentInfo[]>(),
packageEnvsAsName: new Map<string, CmdEnvSuggestion[]>(),
packageEnvsAsMacro: new Map<string, CmdEnvSuggestion[]>(),
packageEnvsForBegin: new Map<string, CmdEnvSuggestion[]>()
packageEnvsForBegin: new Map<string, CmdEnvSuggestion[]>(),
}

lw.onConfigChange('intellisense.package.exclude', initialize)
initialize()
function initialize() {
const excludeDefault = (vscode.workspace.getConfiguration('latex-workshop').get('intellisense.package.exclude') as string[]).includes('lw-default')
const envs = excludeDefault ? [] : (JSON.parse(fs.readFileSync(`${lw.extensionRoot}/data/environments.json`, {encoding: 'utf8'})) as EnvironmentRaw[]).map(env => envRawToInfo('latex', env))
const excludeDefault = (
vscode.workspace.getConfiguration('latex-workshop').get('intellisense.package.exclude') as string[]
).includes('lw-default')
const envs = excludeDefault
? []
: (
JSON.parse(
fs.readFileSync(`${lw.extensionRoot}/data/environments.json`, { encoding: 'utf8' })
) as EnvironmentRaw[]
).map((env) => envRawToInfo('latex', env))
data.defaultEnvsAsMacro = []
data.defaultEnvsForBegin = []
data.defaultEnvsAsName = []
envs.forEach(env => {
envs.forEach((env) => {
data.defaultEnvsAsMacro.push(entryEnvToCompletion(env, EnvSnippetType.AsMacro))
data.defaultEnvsForBegin.push(entryEnvToCompletion(env, EnvSnippetType.ForBegin))
data.defaultEnvsAsName.push(entryEnvToCompletion(env, EnvSnippetType.AsName))
Expand All @@ -42,7 +57,6 @@ function initialize() {
return data
}


/**
* This function is called by Macro.initialize with type=EnvSnippetType.AsMacro
* to build a `\envname` macro for every default environment.
Expand Down Expand Up @@ -77,28 +91,33 @@ function getPackageEnvs(type?: EnvSnippetType): Map<string, CmdEnvSuggestion[]>
}

function from(result: RegExpMatchArray, args: CompletionArgs) {
const suggestions = provide(args.langId, args.line)
const suggestions = provide(args.langId, args.line, args.position)
// Macros starting with a non letter character are not filtered properly because of wordPattern definition.
return filterNonLetterSuggestions(suggestions, result[1], args.position)
}

function provide(langId: string, line: string): CompletionItem[] {
function provide(langId: string, line: string, position: vscode.Position): CompletionItem[] {
let snippetType: EnvSnippetType = EnvSnippetType.AsName
if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.selections.length === 1 && line.indexOf('\\begin') > line.indexOf('\\end')) {
if (
vscode.window.activeTextEditor &&
vscode.window.activeTextEditor.selections.length === 1 &&
line.indexOf('\\begin') > line.indexOf('\\end') &&
line.slice(position.character).match(/[a-zA-Z*]*}/) === null
) {
snippetType = EnvSnippetType.ForBegin
}

// Extract cached envs and add to default ones
const suggestions: CmdEnvSuggestion[] = Array.from(getDefaultEnvs(snippetType))
const envList: string[] = getDefaultEnvs(snippetType).map(env => env.label)
const envList: string[] = getDefaultEnvs(snippetType).map((env) => env.label)

// Insert package environments
const configuration = vscode.workspace.getConfiguration('latex-workshop')
if (configuration.get('intellisense.package.enabled')) {
const unusual = configuration.get('intellisense.package.unusual') as boolean
const packages = lw.completion.usepackage.getAll(langId)
Object.entries(packages).forEach(([packageName, options]) => {
getEnvFromPkg(packageName, snippetType).forEach(env => {
getEnvFromPkg(packageName, snippetType).forEach((env) => {
if (env.ifCond && !options.includes(env.ifCond)) {
return
}
Expand All @@ -114,11 +133,11 @@ function provide(langId: string, line: string): CompletionItem[] {
}

// Insert environments defined in tex
lw.cache.getIncludedTeX().forEach(cachedFile => {
lw.cache.getIncludedTeX().forEach((cachedFile) => {
const cachedEnvs = lw.cache.get(cachedFile)?.elements.environment
if (cachedEnvs !== undefined) {
cachedEnvs.forEach(env => {
if (! envList.includes(env.label)) {
cachedEnvs.forEach((env) => {
if (!envList.includes(env.label)) {
if (snippetType === EnvSnippetType.ForBegin) {
env.insertText = new vscode.SnippetString(`${env.label}}\n\t$0\n\\end{${env.label}}`)
} else {
Expand All @@ -140,12 +159,17 @@ function provide(langId: string, line: string): CompletionItem[] {
* Environments can be inserted using `\envname`.
* This function is called by Macro.provide to compute these macros for every package in use.
*/
function provideEnvsAsMacroInPkg(packageName: string, options: string[], suggestions: CmdEnvSuggestion[], defined?: Set<string>) {
function provideEnvsAsMacroInPkg(
packageName: string,
options: string[],
suggestions: CmdEnvSuggestion[],
defined?: Set<string>
) {
defined = defined ?? new Set<string>()
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const useOptionalArgsEntries = configuration.get('intellisense.optionalArgsEntries.enabled')

if (! configuration.get('intellisense.package.env.enabled')) {
if (!configuration.get('intellisense.package.env.enabled')) {
return
}

Expand All @@ -158,7 +182,7 @@ function provideEnvsAsMacroInPkg(packageName: string, options: string[], suggest

const unusual = configuration.get('intellisense.package.unusual') as boolean
// Insert env snippets
envs.forEach(env => {
envs.forEach((env) => {
if (!useOptionalArgsEntries && env.hasOptionalArgs()) {
return
}
Expand Down Expand Up @@ -186,8 +210,15 @@ function parse(cache: FileCache) {
function parseAst(node: Ast.Node): CmdEnvSuggestion[] {
let envs: CmdEnvSuggestion[] = []
if (node.type === 'environment' || node.type === 'mathenv') {
const content = (typeof node.env === 'string') ? node.env : (node.env as unknown as {content: string}).content
const env = new CmdEnvSuggestion(`${content}`, '', [], -1, { name: content, args: '' }, vscode.CompletionItemKind.Module)
const content = typeof node.env === 'string' ? node.env : (node.env as unknown as { content: string }).content
const env = new CmdEnvSuggestion(
`${content}`,
'',
[],
-1,
{ name: content, args: '' },
vscode.CompletionItemKind.Module
)
env.documentation = '`' + content + '`'
env.filterText = content
envs.push(env)
Expand Down Expand Up @@ -221,7 +252,14 @@ function parseContent(content: string): CmdEnvSuggestion[] {
if (envList.includes(result[1])) {
continue
}
const env = new CmdEnvSuggestion(`${result[1]}`, '', [], -1, { name: result[1], args: '' }, vscode.CompletionItemKind.Module)
const env = new CmdEnvSuggestion(
`${result[1]}`,
'',
[],
-1,
{ name: result[1], args: '' },
vscode.CompletionItemKind.Module
)
env.documentation = '`' + result[1] + '`'
env.filterText = result[1]

Expand All @@ -246,7 +284,7 @@ function getEnvFromPkg(packageName: string, type: EnvSnippetType): CmdEnvSuggest
}

const newEntry: CmdEnvSuggestion[] = []
pkgEnvs.forEach(env => {
pkgEnvs.forEach((env) => {
// \array{} : detail=array{}, name=array.
newEntry.push(entryEnvToCompletion(env, type))
})
Expand All @@ -264,7 +302,10 @@ function envRawToInfo(packageName: string, env: EnvironmentRaw): EnvironmentInfo
}

function setPackageEnvs(packageName: string, envs: EnvironmentRaw[]) {
data.packageEnvs.set(packageName, envs.map(env => envRawToInfo(packageName, env)))
data.packageEnvs.set(
packageName,
envs.map((env) => envRawToInfo(packageName, env))
)
}

function entryEnvToCompletion(item: EnvironmentInfo, type: EnvSnippetType): CmdEnvSuggestion {
Expand All @@ -277,8 +318,11 @@ function entryEnvToCompletion(item: EnvironmentInfo, type: EnvSnippetType): CmdE
{ name: item.name, args: item.arg?.format ?? '' },
vscode.CompletionItemKind.Module,
item.if,
item.unusual)
suggestion.detail = `\\begin{${item.name}}${item.arg?.snippet.replace(/\$\{\d+:([^$}]*)\}/g, '$1') ?? ''}\n...\n\\end{${item.name}}`
item.unusual
)
suggestion.detail = `\\begin{${item.name}}${
item.arg?.snippet.replace(/\$\{\d+:([^$}]*)\}/g, '$1') ?? ''
}\n...\n\\end{${item.name}}`
suggestion.documentation = `Environment ${item.name} .`
if (item.package) {
suggestion.documentation += ` From package: ${item.package}.`
Expand All @@ -293,7 +337,7 @@ function entryEnvToCompletion(item: EnvironmentInfo, type: EnvSnippetType): CmdE
}
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const useTabStops = configuration.get('intellisense.useTabStops.enabled')
const prefix = (type === EnvSnippetType.ForBegin) ? '' : 'begin{'
const prefix = type === EnvSnippetType.ForBegin ? '' : 'begin{'
let snippet: string = item.arg?.snippet ?? ''
if (item.arg?.snippet && useTabStops) {
snippet = item.arg.snippet.replace(/\$\{(\d+):[^}]*\}/g, '$${$1}')
Expand Down
16 changes: 16 additions & 0 deletions test/units/17_completion_environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ describe(path.basename(__filename).split('.')[0] + ':', () => {
assert.ok(suggestion.insertText.value.startsWith('itemize}\n'), suggestion.insertText.value)
})

it('should provide environment name when the cursor is `\\begin{|}`', () => {
const stub = mock.activeTextEditor(texPath, '\\begin{}')
const suggestion = provider
.from(['\\begin{', ''], {
uri: vscode.Uri.file(texPath),
langId: 'latex',
line: '\\begin{}',
position: new vscode.Position(0, 7),
})
.find(s => s.label === 'itemize')
stub.restore()

assert.ok(suggestion)
assert.strictEqual(suggestion.insertText, undefined)
})

it('should provide environment name when the cursor is `\\begin{|}\\end{|}`', () => {
const editor = new TextEditor(texPath, '\\begin{}\\end{}', {})
editor.setSelections([
Expand Down

0 comments on commit 4c1e1b4

Please sign in to comment.