Skip to content

Commit

Permalink
Merge pull request #3753 from Hannah-Sten/line-breaks-formatting
Browse files Browse the repository at this point in the history
Indent content between \left and \right
  • Loading branch information
PHPirates authored Dec 3, 2024
2 parents 82e8ff0 + 8610a14 commit 89e2057
Show file tree
Hide file tree
Showing 13 changed files with 110 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import com.intellij.formatting.ChildAttributes
import com.intellij.formatting.Indent
import com.intellij.lang.ASTNode
import nl.hannahsten.texifyidea.formatting.LatexBlock
import nl.hannahsten.texifyidea.psi.*
import nl.hannahsten.texifyidea.psi.LatexBeginCommand
import nl.hannahsten.texifyidea.psi.LatexEnvironment
import nl.hannahsten.texifyidea.psi.LatexParameterText
import nl.hannahsten.texifyidea.psi.LatexTypes
import nl.hannahsten.texifyidea.settings.codestyle.LatexCodeStyleSettings
import nl.hannahsten.texifyidea.util.parser.firstChildOfType

Expand All @@ -30,9 +33,10 @@ object LatexEnterHandler {
}

val type = node.elementType
if (type === LatexTypes.DISPLAY_MATH || shouldIndentEnvironment) {
if (type == LatexTypes.DISPLAY_MATH || shouldIndentEnvironment || type == LatexTypes.LEFT_RIGHT) {
return ChildAttributes(Indent.getNormalIndent(true), null)
}

val indentSections = CodeStyle.getCustomSettings(node.psi.containingFile, LatexCodeStyleSettings::class.java).INDENT_SECTIONS
if (indentSections) {
// This function will be called on the block for which the caret is adding something in the children at the given index,
Expand Down
4 changes: 4 additions & 0 deletions src/nl/hannahsten/texifyidea/formatting/LatexBlock.kt
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ class LatexBlock(
return Indent.getNormalIndent(false)
}

if (myNode.elementType == LatexTypes.LEFT_RIGHT_CONTENT) {
return Indent.getNormalIndent(true)
}

// Display math
return if ((myNode.elementType === LatexTypes.MATH_CONTENT || myNode.elementType === LatexTypes.COMMENT_TOKEN) &&
myNode.treeParent?.elementType === LatexTypes.DISPLAY_MATH
Expand Down
20 changes: 13 additions & 7 deletions src/nl/hannahsten/texifyidea/grammar/Latex.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ latexFile ::= content
content ::= no_math_content*

// When updating this list, consider updating other _content lists
no_math_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | group | normal_text | END_IF | ELSE
no_math_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | left_right | group | normal_text | END_IF | ELSE | LEFT | RIGHT

normal_text ::= (NORMAL_TEXT_WORD | STAR | AMPERSAND | QUOTATION_MARK | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | OPEN_PAREN | CLOSE_PAREN | OPEN_BRACKET | CLOSE_BRACKET | PIPE | EXCLAMATION_MARK | BACKSLASH | EQUALS | COMMA | ANGLE_PARAM)+

Expand All @@ -80,7 +80,7 @@ pseudocode_block_content ::= no_math_content*
if_block ::= START_IF if_block_content? (ELSE if_block_content?)* END_IF { pin=1 }

// no_math_content without end_if
if_block_content ::= (raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | group | normal_text)*
if_block_content ::= (raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | left_right | group | normal_text | LEFT | RIGHT)*

commands ::= COMMAND_TOKEN STAR? parameter* {
pin=1
Expand All @@ -91,6 +91,11 @@ commands ::= COMMAND_TOKEN STAR? parameter* {
implements=["com.intellij.psi.PsiNameIdentifierOwner" "nl.hannahsten.texifyidea.psi.LatexCommandWithParams"]
}

// To allow indenting the content between left( and right), we need it as a separate element
// This is no_math_content without the loose left and right, we need this to make sure a complete left_right situation is parsed as a left_right and not as two separate left and right
left_right_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | left_right | group | normal_text | END_IF | ELSE
// Allow unmatched left and right
left_right ::= LEFT (OPEN_PAREN | OPEN_ANGLE_BRACKET | OPEN_BRACE | OPEN_BRACKET | PIPE)? left_right_content* RIGHT (CLOSE_PAREN | CLOSE_ANGLE_BRACKET | CLOSE_BRACE | CLOSE_BRACKET | PIPE)?

begin_command ::= BEGIN_TOKEN STAR? parameter* {
pin=1
Expand Down Expand Up @@ -120,15 +125,16 @@ optional_key_val_key ::= optional_param_content+ {
// a key and a value.
required_param ::= OPEN_BRACE (strict_key_val_pair (COMMA strict_key_val_pair)* CLOSE_BRACE | required_param_content* CLOSE_BRACE) { pin=1 }

// Picture environment (and derived environments) from beamer uses parentheses for arguments
picture_param ::= OPEN_PAREN picture_param_content* CLOSE_PAREN { pin=3 }

// These are like content, but no brackets and with parameter_text instead of normal_text
// We have to separate optional and required parameter content, because required parameter content
// can contain mismatched brackets, but optional parameters not (then we wouldn't know what to match)
optional_param_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | group | OPEN_PAREN | CLOSE_PAREN | parameter_text | BACKSLASH | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | END_IF | ELSE
required_param_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | group | OPEN_PAREN | CLOSE_PAREN | parameter_text | COMMA | EQUALS | OPEN_BRACKET | CLOSE_BRACKET | BACKSLASH | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | END_IF | ELSE | ANGLE_PARAM
optional_param_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | left_right | group | OPEN_PAREN | CLOSE_PAREN | parameter_text | BACKSLASH | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | END_IF | ELSE | LEFT | RIGHT
required_param_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | group | OPEN_PAREN | CLOSE_PAREN | parameter_text | COMMA | EQUALS | OPEN_BRACKET | CLOSE_BRACKET | BACKSLASH | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | END_IF | ELSE | ANGLE_PARAM | LEFT | RIGHT
// Cannot contain ( or )
picture_param_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | group | parameter_text | BACKSLASH | COMMA | EQUALS | OPEN_BRACKET | CLOSE_BRACKET | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | END_IF | ELSE
picture_param_content ::= raw_text | magic_comment | comment | environment | pseudocode_block | if_block | math_environment | COMMAND_IFNEXTCHAR | commands | left_right | group | parameter_text | BACKSLASH | COMMA | EQUALS | OPEN_BRACKET | CLOSE_BRACKET | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | END_IF | ELSE | LEFT | RIGHT

strict_key_val_pair ::= key_val_key EQUALS key_val_value?

Expand All @@ -139,15 +145,15 @@ key_val_key ::= (group | NORMAL_TEXT_WORD | STAR | AMPERSAND | QUOTATION_MARK |
key_val_value ::= key_val_content+ {
mixin="nl.hannahsten.texifyidea.psi.impl.LatexKeyValValueImplMixin"
}
key_val_content ::= parameter_text | parameter_group | OPEN_PAREN | CLOSE_PAREN | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | commands | math_environment
key_val_content ::= parameter_text | parameter_group | OPEN_PAREN | CLOSE_PAREN | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | commands | left_right | math_environment


// The lowest level of a parameter must have the getReferences etc. implemented
// We don't do this on normal_text because then every normal_text in the document would be a reference
// So, the following is like normal_text
// This assumes that parameter text which is a reference, appears directly under param_content
// Commands is here instead of in required_param_content because it can be part of reference text for example to a file
parameter_text ::= (commands | NORMAL_TEXT_WORD | STAR | AMPERSAND | QUOTATION_MARK | PIPE | EXCLAMATION_MARK)+ {
parameter_text ::= (commands | left_right | NORMAL_TEXT_WORD | STAR | AMPERSAND | QUOTATION_MARK | PIPE | EXCLAMATION_MARK | ELSE | LEFT | RIGHT)+ {
mixin="nl.hannahsten.texifyidea.psi.impl.LatexParameterTextImplMixin"
}

Expand Down
5 changes: 5 additions & 0 deletions src/nl/hannahsten/texifyidea/grammar/LatexLexer.flex
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ NEWENVIRONMENT=\\(re)?newenvironment
// BeforeBegin/AfterEnd are from etoolbox, and just happen to also have two parameters where the second can contain loose \begin or \end
NEWCOMMAND=\\(new|provide)command | \\BeforeBeginEnvironment | \\AfterEndEnvironment
NEWDOCUMENTENVIRONMENT=\\(New|Renew|Provide|Declare)DocumentEnvironment
// These are separate to support formatting
LEFT=\\left
RIGHT=\\right

// Verbatim commands which will be delimited by the same character
// \path from the 'path' package
Expand Down Expand Up @@ -525,6 +528,8 @@ END_IFS=\\fi
{CLOSE_BRACE} { return CLOSE_BRACE; }
{OPEN_PAREN} { return OPEN_PAREN; }
{CLOSE_PAREN} { return CLOSE_PAREN; }
{LEFT} { return LEFT; }
{RIGHT} { return RIGHT; }

{LEXER_OFF_TOKEN} { yypushState(OFF); return COMMENT_TOKEN; }
{ENDINPUT} { yypushState(OFF); return COMMAND_TOKEN; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class LatexParserDefinition : ParserDefinition {
val FILE: IStubFileElementType<*> = object : IStubFileElementType<LatexFileStub>(
"LatexStubFileElementType", Language.findInstance(LatexLanguage::class.java)
) {
override fun getStubVersion(): Int = 76
override fun getStubVersion(): Int = 77
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/nl/hannahsten/texifyidea/highlighting/LatexAnnotator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.elementType
import com.intellij.psi.util.endOffset
import com.intellij.psi.util.startOffset
import nl.hannahsten.texifyidea.index.LatexDefinitionIndex
Expand Down Expand Up @@ -69,6 +70,12 @@ open class LatexAnnotator : Annotator {
else if (psiElement is LatexCommands) {
annotateCommands(psiElement, annotationHolder)
}
else if (psiElement.elementType == LatexTypes.LEFT || psiElement.elementType == LatexTypes.RIGHT) {
annotationHolder.newAnnotation(HighlightSeverity.INFORMATION, "")
.range(psiElement)
.textAttributes(LatexSyntaxHighlighter.COMMAND_MATH_DISPLAY)
.create()
}
else if (psiElement.isComment()) {
annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(psiElement.textRange)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ class LatexSyntaxHighlighter : SyntaxHighlighterBase() {
LatexTypes.START_IF,
LatexTypes.ELSE,
LatexTypes.END_IF,
LatexTypes.LEFT,
LatexTypes.RIGHT,
)

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class LatexSpellcheckingStrategy : SpellcheckingStrategy() {
if (psiElement.elementType == LatexTypes.COMMAND_TOKEN ||
psiElement.elementType == LatexTypes.COMMAND_IFNEXTCHAR ||
psiElement.elementType == LatexTypes.COMMENT_TOKEN ||
psiElement.elementType == LatexTypes.LEFT ||
psiElement.elementType == LatexTypes.RIGHT ||
isBeginEnd(psiElement) ||
psiElement.hasParent(LatexOptionalParam::class)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import nl.hannahsten.texifyidea.lang.Diacritic
import nl.hannahsten.texifyidea.lang.magic.MagicCommentScope
import nl.hannahsten.texifyidea.psi.LatexMathContent
import nl.hannahsten.texifyidea.psi.LatexNormalText
import nl.hannahsten.texifyidea.psi.LatexTypes
import nl.hannahsten.texifyidea.util.parser.commandTokens
import nl.hannahsten.texifyidea.util.parser.hasParent
import nl.hannahsten.texifyidea.util.parser.inMathContext
import nl.hannahsten.texifyidea.util.parser.isComment
Expand Down Expand Up @@ -70,7 +70,7 @@ open class LatexDiacriticIJInspection : TexifyRegexInspection(
val offset = matcher.end()

val foundAhead = file.findElementAt(offset)
if (foundAhead is LeafPsiElement && foundAhead.elementType == LatexTypes.COMMAND_TOKEN) {
if (foundAhead is LeafPsiElement && commandTokens.contains(foundAhead.elementType)) {
return false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class LatexUsagesProvider : FindUsagesProvider {
LatexTypes.BEGIN_COMMAND, LatexTypes.BEGIN_TOKEN,
LatexTypes.END_COMMAND, LatexTypes.END_TOKEN,
LatexTypes.PARAMETER_TEXT, LatexTypes.PARAMETER,
LatexTypes.REQUIRED_PARAM, LatexTypes.OPTIONAL_PARAM
LatexTypes.REQUIRED_PARAM, LatexTypes.OPTIONAL_PARAM,
LatexTypes.LEFT, LatexTypes.RIGHT,
),
// Comments.
TokenSet.create(LatexTypes.COMMENT_TOKEN, LatexTypes.COMMENT),
Expand Down
11 changes: 2 additions & 9 deletions src/nl/hannahsten/texifyidea/util/parser/Psi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.nextLeaf
import com.intellij.util.ProcessingContext
Expand Down Expand Up @@ -194,14 +193,6 @@ fun PsiElement?.findOuterMathEnvironment(): PsiElement? {
return outerMathEnvironment
}

/**
* Check if the element is in a comment or not.
*/
fun PsiElement.inComment() = inDirectEnvironmentContext(Environment.Context.COMMENT) || when (this) {
is PsiComment -> true
else -> this is LeafPsiElement && elementType == LatexTypes.COMMAND_TOKEN
}

/**
* Checks if the element is inside a verbatim context.
*/
Expand Down Expand Up @@ -323,6 +314,8 @@ inline fun PsiElement.hasParentMatching(maxDepth: Int, predicate: (PsiElement) -
return false
}

val commandTokens = setOf(LatexTypes.COMMAND_TOKEN, LatexTypes.LEFT, LatexTypes.RIGHT)

/**
* Checks whether the psi element is part of a comment or not.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,32 @@ class LatexTypedHandlerTest : BasePlatformTestCase() {
)
}

fun testLeftRight() {
myFixture.configureByText(
LatexFileType,
"""
\[
\left(
a+b<caret>
\xi
\right)
\]
""".trimIndent()
)
myFixture.type("\n+c")
myFixture.checkResult(
"""
\[
\left(
a+b
+c<caret>
\xi
\right)
\]
""".trimIndent()
)
}

fun testBracesCompletion() {
myFixture.configureByText(LatexFileType, """\mycommand<caret>""")
myFixture.type("{")
Expand Down
38 changes: 38 additions & 0 deletions test/nl/hannahsten/texifyidea/formatting/LatexFormattingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,44 @@ class LatexFormattingTest : BasePlatformTestCase() {
""".trimIndent()
}

fun testPictureParameter() {
"""
\begin{textblock*}{\paperwidth}[0.5,0.5](0.5\paperwidth,0.5\paperheight)
Thank you for your attention.
\end{textblock*}
""".trimIndent() `should be reformatted to` """
\begin{textblock*}{\paperwidth}[0.5,0.5](0.5\paperwidth,0.5\paperheight)
Thank you for your attention.
\end{textblock*}
""".trimIndent()
}

fun testLeftRight() {
"""
\[
\left(
\xi
a + b
+ c
\left[
\frac a b
\right]
\right)
\]
""".trimIndent() `should be reformatted to` """
\[
\left(
\xi
a + b
+ c
\left[
\frac a b
\right]
\right)
\]
""".trimIndent()
}

fun `test formatter off and on comments`() {
"""
% @formatter:off
Expand Down

0 comments on commit 89e2057

Please sign in to comment.