All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.textmate.lexer.TextmateLexer Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.textmate.lexer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.tm4e.core.grammar.IGrammar;
import org.eclipse.tm4e.core.grammar.IToken;
import org.eclipse.tm4e.core.grammar.ITokenizeLineResult;
import org.eclipse.tm4e.core.grammar.StackElement;
import org.netbeans.api.lexer.Token;
import org.netbeans.spi.lexer.Lexer;
import org.netbeans.spi.lexer.LexerInput;
import org.netbeans.spi.lexer.TokenFactory;

public class TextmateLexer implements Lexer{

    private static final Object DO_NOT_RESUME_HERE = new Object();

    private final LexerInput li;
    private final TokenFactory factory;
    private final IGrammar grammar;
    private int lineLen;
    private int currentOffset;
    private List lineTokens;
    private int currentIdx;
    private StackElement state;
    private boolean forceReadLine;

    public TextmateLexer(LexerInput li, Object state, TokenFactory factory, IGrammar grammar) {
        this.li = li;
        this.factory = factory;
        this.grammar = grammar;
        if (state instanceof IntralineState) {
            IntralineState istate = (IntralineState) state;
            this.lineLen = istate.lineLen;
            this.currentOffset = istate.currentOffset;
            this.lineTokens = istate.lineTokens;
            this.currentIdx = istate.currentIdx;
            this.state = istate.state;
            this.forceReadLine = true;
        } else {
            this.state = (StackElement) state;
        }
    }

    @Override
    public Token nextToken() {
        if (currentOffset >= lineLen || forceReadLine) {
            //read next line:
            int read;
            while ((read = li.read()) != LexerInput.EOF && read != '\n');
            if (!forceReadLine) {
                if (li.readLength() != 0) {
                    lineLen = li.readText().length();
                    currentOffset = 0;
                    ITokenizeLineResult tokenized = grammar.tokenizeLine(li.readText().toString(), state);
                    lineTokens = new ArrayList<>(Arrays.asList(tokenized.getTokens()));
                    currentIdx = 0;
                    state = tokenized.getRuleStack();
                } else {
                    lineTokens = null;
                }
            }
            forceReadLine = false;
        }
        if (lineTokens != null && currentIdx < lineTokens.size()) {
            IToken current = lineTokens.get(currentIdx);
            if (currentOffset < current.getStartIndex()) {
                int len = current.getStartIndex() - currentOffset;
                currentOffset += len;
                return factory.createToken(TextmateTokenId.UNTOKENIZED, lineLen - currentOffset);
            } else if (currentOffset == current.getStartIndex()) {
                currentIdx++;
                int len = current.getEndIndex() - current.getStartIndex();
                len = Math.min(len, lineLen -  currentOffset); //XXX: untested, unclear when this happens (Rust)
                currentOffset += len;
                List categories = Collections.unmodifiableList(new ArrayList<>(current.getScopes()));
                return factory.createPropertyToken(TextmateTokenId.TEXTMATE, len, (token, key) -> {
                    if ("categories".equals(key)) {
                        return categories;
                    } else {
                        return null;
                    }
                });
            }
        }
        if (currentOffset < lineLen) {
            int len = lineLen - currentOffset;
            currentOffset += len;
            return factory.createToken(TextmateTokenId.UNTOKENIZED, len);
        }
        return null;
    }
    
    @Override
    public Object state() {
        return lineLen != currentOffset ? new IntralineState(lineLen, currentOffset, lineTokens, currentIdx, state) : this.state;
    }

    @Override
    public void release() {}
    
    private static final class IntralineState {
        private int lineLen;
        private int currentOffset;
        private List lineTokens;
        private int currentIdx;
        private StackElement state;

        public IntralineState(int lineLen, int currentOffset, List lineTokens, int currentIdx, StackElement state) {
            this.lineLen = lineLen;
            this.currentOffset = currentOffset;
            this.lineTokens = lineTokens;
            this.currentIdx = currentIdx;
            this.state = state;
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy