mirror of
https://github.com/James-Yu/LaTeX-Workshop.git
synced 2024-10-04 15:17:59 +03:00
Add Close / Navigate to environment actions
This commit is contained in:
parent
0b555a6dc8
commit
8c3f6b9f0a
15
.vscode/settings.json
vendored
15
.vscode/settings.json
vendored
@ -8,5 +8,18 @@
|
||||
},
|
||||
"typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version
|
||||
"vsicons.presets.angular": false,
|
||||
"tslint.exclude": "node_modules"
|
||||
"tslint.exclude": "node_modules",
|
||||
"cSpell.enabledLanguageIds": [
|
||||
"csharp",
|
||||
"go",
|
||||
"handlebars",
|
||||
"html",
|
||||
"latex",
|
||||
"markdown",
|
||||
"plaintext",
|
||||
"restructuredtext",
|
||||
"text",
|
||||
"typescriptreact",
|
||||
"yml"
|
||||
]
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ LaTeX Workshop is an extension for [Visual Studio Code](https://code.visualstudi
|
||||
- Real-time linting of LaTeX with ChkTeX to pick up common LaTeX issues as you type.
|
||||
- Code Actions (automatic fixes) are offered for many issues found by ChkTeX.
|
||||
- LaTeX file formatting.
|
||||
- Auto close LaTeX environments: just call _LaTeX Workshop: Close current environment_ from the **Command Palette**. You can easily assign a shortcut to this action through the **Keyboard Shortcuts** menu, search for `latex-workshop.close-env`.
|
||||
- Navigate from `\begin/\end` to the corresponding `\end/\begin`: while on the `begin` or `end` keyword, call _LaTeX Workshop: Navigate to matching begin/end_ from the **Command Palette**. To define a shortcut, search for `latex-workshop.navigate-envpair` in the **Keyboard Shortcuts** menu.
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
|
10
package.json
10
package.json
@ -149,6 +149,16 @@
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "latex-workshop.navigate-envpair",
|
||||
"title": "Navigate to matching begin/end",
|
||||
"category": "LaTeX Workshop"
|
||||
},
|
||||
{
|
||||
"command": "latex-workshop.close-env",
|
||||
"title": "Close current environment",
|
||||
"category": "LaTeX Workshop"
|
||||
},
|
||||
{
|
||||
"command": "latex-workshop.build",
|
||||
"title": "Build LaTeX project",
|
||||
|
@ -225,6 +225,22 @@ export class Commander {
|
||||
})
|
||||
}
|
||||
|
||||
navigateToEnvPair() {
|
||||
this.extension.logger.addLogMessage(`JumpToEnvPair command invoked.`)
|
||||
if (!vscode.window.activeTextEditor || !this.extension.manager.isTex(vscode.window.activeTextEditor.document.fileName)) {
|
||||
return
|
||||
}
|
||||
this.extension.envPair.gotoPair()
|
||||
}
|
||||
|
||||
closeEnv() {
|
||||
this.extension.logger.addLogMessage(`CloseEnv command invoked.`)
|
||||
if (!vscode.window.activeTextEditor || !this.extension.manager.isTex(vscode.window.activeTextEditor.document.fileName)) {
|
||||
return
|
||||
}
|
||||
this.extension.envPair.closeEnv()
|
||||
}
|
||||
|
||||
actions() {
|
||||
this.extension.logger.addLogMessage(`ACTIONS command invoked.`)
|
||||
const configuration = vscode.workspace.getConfiguration('latex-workshop')
|
||||
|
167
src/components/envpair.ts
Normal file
167
src/components/envpair.ts
Normal file
@ -0,0 +1,167 @@
|
||||
import * as vscode from 'vscode'
|
||||
import { Extension } from '../main'
|
||||
|
||||
/**
|
||||
* Remove the comments if any
|
||||
* @param line
|
||||
*/
|
||||
function stripComments(line: string) : string {
|
||||
let commentPos = line.search(/(?!\\)%/)
|
||||
if (commentPos !== -1) {
|
||||
commentPos++
|
||||
return line.slice(0, commentPos)
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
|
||||
}
|
||||
|
||||
function regexpAllMatches(str: string, reg: RegExp) {
|
||||
let m
|
||||
const res: any[] = []
|
||||
while (m = reg.exec(str)) {
|
||||
res.push(m)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
interface MatchPair {
|
||||
str: string
|
||||
pos: vscode.Position
|
||||
}
|
||||
|
||||
interface MatchEnv {
|
||||
name: string
|
||||
type: string // 'begin' or 'end'
|
||||
pos: vscode.Position
|
||||
}
|
||||
|
||||
export class EnvPair {
|
||||
extension: Extension
|
||||
beginLength = '\\begin'.length
|
||||
endLength = '\\end'.length
|
||||
|
||||
constructor(extension: Extension) {
|
||||
this.extension = extension
|
||||
}
|
||||
|
||||
getEnvName(line: string, ind: number, beginOrEnd: string) : string | null {
|
||||
const subline = line.slice(ind)
|
||||
const re = new RegExp('^' + beginOrEnd + '\\{([^\\{\\}]*)\\}')
|
||||
const env = subline.match(re)
|
||||
if (env && env.length === 2) {
|
||||
return env[1]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
tokenizeLine(document: vscode.TextDocument, pos: vscode.Position) : MatchEnv | null {
|
||||
const line = stripComments(document.lineAt(pos).text)
|
||||
const ind = pos.character
|
||||
if (ind > line.length) {
|
||||
console.log('We are in a comment')
|
||||
return null
|
||||
}
|
||||
const lineUpToInd = line.slice(0, ind + 1)
|
||||
const startInd = lineUpToInd.lastIndexOf('\\')
|
||||
const startPos = new vscode.Position(pos.line, startInd)
|
||||
if (startInd + this.beginLength >= ind && line.slice(startInd, startInd + this.beginLength) === '\\begin') {
|
||||
const envName = this.getEnvName(line, startInd, '\\\\begin')
|
||||
if (envName) {
|
||||
return {pos: startPos, type: 'begin', name: envName}
|
||||
}
|
||||
} else if (startInd + this.endLength >= ind && line.slice(startInd, startInd + this.endLength) === '\\end') {
|
||||
const envName = this.getEnvName(line, startInd, '\\\\end')
|
||||
if (envName) {
|
||||
return {pos: startPos, type: 'end', name: envName}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
locateMatchingPair(pattern: string, dir: number, pos: vscode.Position, doc: vscode.TextDocument) : MatchPair | null {
|
||||
const patRegexp = new RegExp(pattern, 'g')
|
||||
let lineNumber = pos.line
|
||||
let nested = 0
|
||||
let line = doc.lineAt(lineNumber).text
|
||||
/* Drop the pattern on the current line */
|
||||
if (dir === 1) {
|
||||
line = line.slice(line.indexOf('}', pos.character) + 1)
|
||||
} else if (dir === -1) {
|
||||
line = line.slice(0, pos.character)
|
||||
}
|
||||
while (true) {
|
||||
line = stripComments(line)
|
||||
let allMatches = regexpAllMatches(line, patRegexp)
|
||||
if (dir === -1) {
|
||||
allMatches = allMatches.reverse()
|
||||
}
|
||||
for (let m of allMatches) {
|
||||
if ((m[1] === 'begin' && dir === 1) || (m[1] === 'end' && dir === -1)) {
|
||||
nested += 1
|
||||
}
|
||||
if ((m[1] === 'end' && dir === 1) || (m[1] === 'begin' && dir === -1)) {
|
||||
if (nested === 0) {
|
||||
const matchPos = new vscode.Position(lineNumber, m.index + 1)
|
||||
const matchStr = m[0]
|
||||
return {str: matchStr, pos: matchPos}
|
||||
}
|
||||
nested -= 1
|
||||
}
|
||||
}
|
||||
lineNumber += dir
|
||||
if (lineNumber < 0 || lineNumber >= doc.lineCount) {
|
||||
break
|
||||
}
|
||||
line = doc.lineAt(lineNumber).text
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
gotoPair() {
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor || editor.document.languageId !== 'latex') {
|
||||
return
|
||||
}
|
||||
const curPos = editor.selection.active
|
||||
const document = editor.document
|
||||
|
||||
const tokens = this.tokenizeLine(document, curPos)
|
||||
if (!tokens) {
|
||||
return
|
||||
}
|
||||
const startPos = tokens.pos
|
||||
const pattern = '\\\\(begin|end)\\{' + escapeRegExp(tokens.name) + '\\}'
|
||||
const dir = (tokens.type === 'begin') ? 1 : -1
|
||||
const resMatchingPair = this.locateMatchingPair(pattern, dir, startPos, document)
|
||||
if (resMatchingPair) {
|
||||
const newPos = resMatchingPair.pos
|
||||
editor.selection = new vscode.Selection(newPos, newPos)
|
||||
editor.revealRange(new vscode.Range(newPos, newPos))
|
||||
}
|
||||
}
|
||||
|
||||
closeEnv() {
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor || editor.document.languageId !== 'latex') {
|
||||
return
|
||||
}
|
||||
const document = editor.document
|
||||
const curPos = editor.selection.active
|
||||
|
||||
const pattern = '\\\\(begin|end)\\{[^\\{\\}]*\\}'
|
||||
const dir = -1
|
||||
const resMatchingPair = this.locateMatchingPair(pattern, dir, curPos, document)
|
||||
if (resMatchingPair) {
|
||||
const endEnv = resMatchingPair.str.replace('begin', 'end')
|
||||
const edits = [vscode.TextEdit.insert(curPos, endEnv)]
|
||||
const uri = document.uri
|
||||
const edit = new vscode.WorkspaceEdit()
|
||||
edit.set(uri, edits)
|
||||
vscode.workspace.applyEdit(edit)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,7 @@ import {Linter} from './components/linter'
|
||||
import {Cleaner} from './components/cleaner'
|
||||
import {Counter} from './components/counter'
|
||||
import {TeXMagician} from './components/texmagician'
|
||||
import {EnvPair} from './components/envpair'
|
||||
|
||||
import {Completer} from './providers/completion'
|
||||
import {CodeActions} from './providers/codeactions'
|
||||
@ -141,6 +142,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand('latex-workshop.log', () => extension.commander.log())
|
||||
vscode.commands.registerCommand('latex-workshop.code-action', (d, r, c, m) => extension.codeActions.runCodeAction(d, r, c, m))
|
||||
vscode.commands.registerCommand('latex-workshop.goto-section', (filePath, lineNumber) => extension.commander.gotoSection(filePath, lineNumber))
|
||||
vscode.commands.registerCommand('latex-workshop.navigate-envpair', () => extension.commander.navigateToEnvPair())
|
||||
vscode.commands.registerCommand('latex-workshop.close-env', () => extension.commander.closeEnv())
|
||||
|
||||
const formatter = new LatexFormatterProvider(extension)
|
||||
vscode.languages.registerDocumentFormattingEditProvider('latex', formatter)
|
||||
@ -259,6 +262,7 @@ export class Extension {
|
||||
codeActions: CodeActions
|
||||
nodeProvider: SectionNodeProvider
|
||||
texMagician: TeXMagician
|
||||
envPair: EnvPair
|
||||
|
||||
constructor() {
|
||||
this.extensionRoot = path.resolve(`${__dirname}/../../`)
|
||||
@ -277,6 +281,7 @@ export class Extension {
|
||||
this.codeActions = new CodeActions(this)
|
||||
this.nodeProvider = new SectionNodeProvider(this)
|
||||
this.texMagician = new TeXMagician(this)
|
||||
this.envPair = new EnvPair(this)
|
||||
|
||||
this.logger.addLogMessage(`LaTeX Workshop initialized.`)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user