2

I am using ultisnips with Gilles Castel's subscript snippet along with a snippet I found on SE to create matrices in latex rather quickly. Unfortunately, I can't use the matrices snippet because it keeps triggering the subscript snippet and I'm not sure how to make the two work together. The snippet for matrices is defined as follows, (there is a separate python function it calls which can be found here)

priority 1100
pre_expand "create_matrix(snip)"
snippet "(small|[bBpvV])?mat(rix)?(\d+)x(\d+)" "Generate (small|[bBpvV])?matrix of *rows* by *columns*" br
endsnippet

The snippets for the subscripting are defined as follows,

context "math()"
snippet '([A-Za-z])(\d)' "auto subscript" wrA
`!p snip.rv = match.group(1)`_`!p snip.rv = match.group(2)`
endsnippet

context "math()"
snippet '([A-Za-z])_(\d\d)' "auto subscript2" wrA
`!p snip.rv = match.group(1)`_{`!p snip.rv = match.group(2)`}
endsnippet

Unfortunately, whenever I try to use the first snippet, e.g. by typing

bmat3x3<TAB>

The subscript snippet triggers and instead outputs

bmat3x_3

Is there any way I can make it so that the subscript snippet does not trigger in this case? I was thinking perhaps there's a way to check if the current word contains "bmat". I'm sorry I'm not familiar with regular expressions as I feel like if I was I could write some sort of if statement evaluating the word to only trigger when the word did not contain bmat.

Thanks.

  • 1
    Welcome to this site! I'm not an expert with snippets plugins but I think you should put a link to Gilles Castels snippet so that people know easily what you are referring to. Also I suppose you are using ultisnips because of the tag of your question but it would be worth it to explicit that in your question. – statox Oct 1 '19 at 9:00
  • 1
    Negative lookbehind assertions can help you. However, there is a limitation: the contained pattern must only match strings of some fixed length. This ([A-Za-z]*)(?<![bBpvV]mat)(\d) is allowed but not ([A-Za-z]*)(?<!(small|[bBpvV])?mat(rix))(\d). This means you have to write it differently, e.g. ([A-Za-z]*)(?<![bBpvV]mat)(?<![^A-Za-z]mat)(\d). I would say this becomes unmanageable. If someone else has a better idea, I am happy to see it. – Hotschke Oct 2 '19 at 14:27
  • 1
    I am not completely sure and haven't checked, but if they are both triggered under the same conditions (different word still), then change priority and it will solve it. – eyal karni Oct 3 '19 at 23:07
  • @eyalkarni: bmat3 triggers only the 'auto-subscript' snippet and not the matrix snippet. Therefore, I do not think that changing the priority helps. – Hotschke Oct 5 '19 at 7:21
2

I add my comment as answer with the complete snippet file.

Negative Lookbehind Assertion in Python Regular Expressions

Python documentation: https://docs.python.org/3/library/re.html#index-23

(?<!...)

Important limitation:

Similar to positive lookbehind assertions, the contained pattern must only match strings of some fixed length. Patterns which start with negative lookbehind assertions may match at the beginning of the string being searched.

To reduce the length of the regex I assume only the abbreviated form of the snippet is of interest

snippet "(small|[bBpvV])?mat(\d+)x(\d+)"

i.e. without (rix)?.

global !p
texMathZones = ['texMathZone'+x for x in ['A', 'AS', 'B', 'BS', 'C',
'CS', 'D', 'DS', 'E', 'ES', 'F', 'FS', 'G', 'GS', 'H', 'HS', 'I', 'IS',
'J', 'JS', 'K', 'KS', 'L', 'LS', 'DS', 'V', 'W', 'X', 'Y', 'Z']]

texIgnoreMathZones = ['texMathText']

texMathZoneIds = vim.eval('map('+str(texMathZones)+", 'hlID(v:val)')")
texIgnoreMathZoneIds = vim.eval('map('+str(texIgnoreMathZones)+", 'hlID(v:val)')")

ignore = texIgnoreMathZoneIds[0]

def math():
    synstackids = vim.eval("synstack(line('.'), col('.') - (col('.')>=2 ? 1 : 0))")
    try:
        first = next(
            i for i in reversed(synstackids)
            if i in texIgnoreMathZoneIds or i in texMathZoneIds
        )
        return first != ignore
    except StopIteration:
        return False

def create_matrix(snip):

        matrix_str = (snip.buffer[snip.line].split('mat')[0]+'matrix').strip()

        rows = 'x'.join(snip.buffer[snip.line].split("x", 2)[:-1])
        cols = 'x'.join(snip.buffer[snip.line].split("x", 2)[-1:])

        int_val = lambda string: int(''.join(s for s in string if s.isdigit()))

        rows = int_val(rows)
        cols = int_val(cols)

        offset = cols + 1
        old_spacing = snip.buffer[snip.line][:snip.buffer[snip.line].rfind('\t') + 1]

        snip.buffer[snip.line] = ''

        final_str = old_spacing + "\\begin{"+matrix_str+"}\n"

        for i in range(rows):
                final_str += old_spacing + '\t'
                final_str += " & ".join(['$' + str(i * cols + j + offset) for j in range(cols)])

                final_str += " \\\\\\\n"

        final_str += old_spacing + "\\end{"+matrix_str+"}\n$0"

        snip.expand_anon(final_str)


endglobal

context "math()"
snippet '([A-Za-z]*)(?<!smallmat)(?<![bBpvV]mat)(?<![^A-Za-z]mat)(\d)' "auto subscript" wrA
`!p snip.rv = match.group(1)`_`!p snip.rv = match.group(2)`
endsnippet

pre_expand "create_matrix(snip)"
snippet "(small|[bBpvV])?mat(\d+)x(\d+)" "Generate (small|[bBpvV])?matrix of *rows* by *columns*" br
endsnippet

As far as I see it the function math() is based on https://vi.stackexchange.com/a/18494/1292 and the function create_matrix(snip) is from https://vi.stackexchange.com/a/17461/1292.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.