def test_insert_comment_outside_block(self, testufo):
        writer = MarkFeatureWriter()
        testufo.features.text = dedent("""\
            #
            # Automatic Code
            #
            """)
        feaFile = parseLayoutFeatures(testufo)

        assert writer.write(testufo, feaFile)

        testufo.features.text = dedent("""\
            #
            # Automatic Code
            #
            markClass acutecomb <anchor 100 200> @MC_top;
            feature mark {
                lookup mark1 {
                    pos base a <anchor 100 200> mark @MC_top;
                } mark1;

            } mark;
            """)
        feaFile = parseLayoutFeatures(testufo)

        assert writer.write(testufo, feaFile)

        # test append mode
        writer = MarkFeatureWriter(mode="append")
        assert writer.write(testufo, feaFile)
    def test_insert_comment_middle(self, testufo):
        writer = MarkFeatureWriter()
        testufo.features.text = dedent("""\
            markClass acutecomb <anchor 100 200> @MC_top;
            feature mark {
                lookup mark1 {
                    pos base a
                        <anchor 100 200> mark @MC_top;
                } mark1;
                #
                # Automatic Code
                #
                lookup mark2 {
                    pos base a
                        <anchor 150 250> mark @MC_top;
                } mark2;
            } mark;
            """)
        feaFile = parseLayoutFeatures(testufo)

        with pytest.raises(
                InvalidFeaturesData,
                match="Insert marker has rules before and after, feature mark "
                "cannot be inserted.",
        ):
            writer.write(testufo, feaFile)

        # test append mode ignores insert marker
        generated = self.writeFeatures(testufo, mode="append")
        assert str(generated) == dedent("""\
            markClass tildecomb <anchor 100 200> @MC_top;

            feature mark {
                lookup mark2base {
                    pos base a
                        <anchor 100 200> mark @MC_top;
                } mark2base;

                lookup mark2liga {
                    pos ligature f_i
                            <anchor 100 500> mark @MC_top
                        ligComponent
                            <anchor 600 500> mark @MC_top;
                } mark2liga;

            } mark;

            feature mkmk {
                lookup mark2mark_top {
                    @MFS_mark2mark_top = [acutecomb tildecomb];
                    lookupflag UseMarkFilteringSet @MFS_mark2mark_top;
                    pos mark tildecomb
                        <anchor 100 300> mark @MC_top;
                } mark2mark_top;

            } mkmk;
            """)
    def test_write_only_one(self, testufo):
        writer = MarkFeatureWriter(features=["mkmk"])  # only builds "mkmk"
        feaFile = ast.FeatureFile()
        assert writer.write(testufo, feaFile)
        fea = str(feaFile)

        assert "feature mark" not in fea
        assert "feature mkmk" in fea

        writer = MarkFeatureWriter(features=["mark"])  # only builds "mark"
        feaFile = ast.FeatureFile()
        assert writer.write(testufo, feaFile)
        fea = str(feaFile)

        assert "feature mark" in fea
        assert "feature mkmk" not in fea
    def test_mark_mkmk_features(self, testufo):
        writer = MarkFeatureWriter()  # by default both mark + mkmk are built
        feaFile = ast.FeatureFile()
        assert writer.write(testufo, feaFile)

        assert str(feaFile) == dedent("""\
            markClass acutecomb <anchor 100 200> @MC_top;
            markClass tildecomb <anchor 100 200> @MC_top;

            feature mark {
                lookup mark2base {
                    pos base a <anchor 100 200> mark @MC_top;
                } mark2base;

                lookup mark2liga {
                    pos ligature f_i <anchor 100 500> mark @MC_top
                        ligComponent <anchor 600 500> mark @MC_top;
                } mark2liga;

            } mark;

            feature mkmk {
                lookup mark2mark_top {
                    @MFS_mark2mark_top = [acutecomb tildecomb];
                    lookupflag UseMarkFilteringSet @MFS_mark2mark_top;
                    pos mark tildecomb <anchor 100 300> mark @MC_top;
                } mark2mark_top;

            } mkmk;
            """)
    def test_defs_and_lookups_first(self, testufo):
        testufo.newGlyph("circumflexcomb")
        writer = MarkFeatureWriter()
        testufo.features.text = dedent("""\
            feature mkmk {
                # Automatic Code
                # Move acutecomb down and right if preceded by circumflexcomb
                lookup move_acutecomb {
                    lookupflag UseMarkFilteringSet [acutecomb circumflexcomb];
                    pos circumflexcomb acutecomb' <0 20 0 20>;
                } move_acutecomb;
            } mkmk;
            """)
        feaFile = parseLayoutFeatures(testufo)

        assert writer.write(testufo, feaFile)

        assert str(feaFile) == dedent("""\
            markClass acutecomb <anchor 100 200> @MC_top;
            markClass tildecomb <anchor 100 200> @MC_top;

            feature mark {
                lookup mark2base {
                    pos base a
                        <anchor 100 200> mark @MC_top;
                } mark2base;

                lookup mark2liga {
                    pos ligature f_i
                            <anchor 100 500> mark @MC_top
                        ligComponent
                            <anchor 600 500> mark @MC_top;
                } mark2liga;

            } mark;

            feature mkmk {
                lookup mark2mark_top {
                    @MFS_mark2mark_top = [acutecomb tildecomb];
                    lookupflag UseMarkFilteringSet @MFS_mark2mark_top;
                    pos mark tildecomb
                        <anchor 100 300> mark @MC_top;
                } mark2mark_top;

            } mkmk;

            feature mkmk {
                # Move acutecomb down and right if preceded by circumflexcomb
                lookup move_acutecomb {
                    lookupflag UseMarkFilteringSet [acutecomb circumflexcomb];
                    pos circumflexcomb acutecomb' <0 20 0 20>;
                } move_acutecomb;

            } mkmk;
            """)
out_path = sys.argv[2]
if len(sys.argv) == 4:
    stylename = sys.argv[3]
else:
    # e.g. "Bold" in "sources/masters/Rasa-Bold.ufo"
    stylename = re.search(r"Rasa\-([A-z]+)", out_path).group(1)

features = ""

# Generate markClass statements and prepend them
font = Font(out_path)
markClassDefinitions = []
feaFile = FeatureFile()  # A holder for the generated mark features

markWriter = MarkFeatureWriter()
markWriter.write(font, feaFile)
markFeatures = feaFile.asFea()  # Get the generated mark features

reMarkClass = re.compile(r"markClass.*;")
markClassDefinitions = reMarkClass.findall(
    markFeatures)  # Save all the markClass definitions

# Read the input feature file
with open(in_path, "r") as input_fea:
    # Replace $markClasses with the generated markClassDefinitions
    if markClassDefinitions != []:
        features = input_fea.read().replace(
            "$markClasses", "\n".join(markClassDefinitions))
    else:
        features = input_fea.read().replace("$markClasses", "")
    def test_insert_comment_after(self, testufo):
        writer = MarkFeatureWriter()
        testufo.features.text = dedent("""\
            markClass acutecomb <anchor 100 200> @MC_top;
            feature mark {
                lookup mark1 {
                    pos base a <anchor 100 200> mark @MC_top;
                } mark1;
                #
                # Automatic Code
                #
            } mark;
            """)
        feaFile = parseLayoutFeatures(testufo)

        assert writer.write(testufo, feaFile)

        assert str(feaFile) == dedent("""\
            markClass acutecomb <anchor 100 200> @MC_top;
            feature mark {
                lookup mark1 {
                    pos base a <anchor 100 200> mark @MC_top;
                } mark1;

                #
                #
            } mark;

            markClass tildecomb <anchor 100 200> @MC_top;

            feature mark {
                lookup mark2base {
                    pos base a <anchor 100 200> mark @MC_top;
                } mark2base;

                lookup mark2liga {
                    pos ligature f_i <anchor 100 500> mark @MC_top
                        ligComponent <anchor 600 500> mark @MC_top;
                } mark2liga;

            } mark;

            feature mkmk {
                lookup mark2mark_top {
                    @MFS_mark2mark_top = [acutecomb tildecomb];
                    lookupflag UseMarkFilteringSet @MFS_mark2mark_top;
                    pos mark tildecomb <anchor 100 300> mark @MC_top;
                } mark2mark_top;

            } mkmk;
            """)

        # test append mode ignores insert marker
        generated = self.writeFeatures(testufo, mode="append")
        assert str(generated) == dedent("""\
            markClass tildecomb <anchor 100 200> @MC_top;

            feature mark {
                lookup mark2base {
                    pos base a <anchor 100 200> mark @MC_top;
                } mark2base;

                lookup mark2liga {
                    pos ligature f_i <anchor 100 500> mark @MC_top
                        ligComponent <anchor 600 500> mark @MC_top;
                } mark2liga;

            } mark;

            feature mkmk {
                lookup mark2mark_top {
                    @MFS_mark2mark_top = [acutecomb tildecomb];
                    lookupflag UseMarkFilteringSet @MFS_mark2mark_top;
                    pos mark tildecomb <anchor 100 300> mark @MC_top;
                } mark2mark_top;

            } mkmk;
            """)