diff --git a/README.md b/README.md index 15c6a36..6c43cc9 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ A Scheme-like typesetting LISP interpreter and editor that using SILE typesettin ## Dependencies +* Python3 * PyQt5>=5.15 * QScintilla>=2.12 * SILE>=0.10 @@ -20,10 +21,10 @@ see `src/example.pdf` To make a wheel package, run the following command in the root folder: - `python setup.py bdist_wheel` + `python3 setup.py bdist_wheel` and then: `cd dist; ls` -`Clochur-x.y.z-py3-none-any.whl` will in it. +`Clochur-x.y.z-py3-none-any.whl` will be in it. diff --git a/example/manual.clc b/example/manual.clc index 85dbdc9..a99bffa 100644 --- a/example/manual.clc +++ b/example/manual.clc @@ -2,6 +2,28 @@ [script "packages/font-fallback"] [script "packages/grid"] +[define section-var 1] +[define sub-section-var 1] +[define section-title ""] + +% custom macro +[def-syntax section +[[_ x][begin +[set! section-title [str-append [str-append [str section-var] [str ". " ]] [str x]]] +[font [[size "20pt"][weight "800"]] section-title] +[set! section-var [+ section-var 1]] +[set! sub-section-var 1] +]]] + +% custom macro + + +[def-syntax subsection +[[_ x][begin +[set! section-title [str-append [str-append [str sub-section-var] [str ". " ]] [str x]]] +[font [[size "16pt"][weight "600"]] section-title] +[set! sub-section-var [+ sub-section-var 1]] +]]] [docu-para [[class "book"][papersize "b5"]]] @@ -12,9 +34,11 @@ [font-size "20pt" "Welcome to use"] [font [[family "sligeach_orig"][size "20pt"]] "Clóċur"][font-size "20pt" ", a toy editor, toy intepreter and a toy typesetting-engine frontend."] -[call section "What is Clochur?"] +[section "What is Clochur?"] -Clochur, or printed as [str "\""][font-family "sligeach_orig" "Clóċur" ][str "\""] in Irish language (Clóċur as Roman type, which means [q]typesetting[q]), is a toy-lisp typesetting language with a intepreter written in Python, and with a simple Editor written in PyQt and Qscintilla. + + +Clochur, or printed as "\""[font-family "sligeach_orig" "Clóċur"]"\"" in Irish language ([italic "\"CLOW-kur\""] Clóċur as Roman type, which means [q]typesetting[q]), is a toy-lisp typesetting language with a intepreter written in Python 3, and with a simple Editor written in PyQt5 and QScintilla. It generate a XML that is readable for SILE, which is a typesetting engine written in Lua, and it generate PDF with SILE. @@ -28,4 +52,23 @@ The functions that it has (although may be buggy or needed to be tested) is: - count basic arithmetic expression. [str "- lambda function"], function definition. -] \ No newline at end of file + +[section "Simple manual"] + +[subsection "Macro"] + +Using 'def-syntax', you can add your macro to sile. the example is a "custom-section" macro: + +[font-family "Noto Sans Mono CJK TC" +% custom macro +"[def-syntax \"section\" + +[[_ x][begin] ] + +[set! section-title [str-append [str-append [str [str \"section-var\"]] [str \". \" ]] [str x]]] + +[font [[size \"20pt\"][weight \"800\"]] [str \"section-title\"]] + +[set! [str \"section-var\"] [+ [str \"section-var\"] 1]] + +]]]" diff --git a/src/Editor/ClochurLexer.py b/src/Clochur/ClochurLexer.py similarity index 67% rename from src/Editor/ClochurLexer.py rename to src/Clochur/ClochurLexer.py index a8458fe..ec77359 100644 --- a/src/Editor/ClochurLexer.py +++ b/src/Clochur/ClochurLexer.py @@ -4,7 +4,7 @@ import re from PyQt5.Qsci import QsciLexerCustom, QsciScintilla from PyQt5.QtGui import * -from Editor.Parser import Parser +from Clochur.Parser import Parser @@ -34,8 +34,16 @@ class ClochurLexer(QsciLexerCustom): self.QUOTES = ['"'] self.PARENTHESIS = ["[", "]"] - self.PRIMARY = ['define', 'def-syntax' , 'True', 'False', 'lambda', '-', '+', - '*', '/', '>' ,'=','<','>=','<=', 'if', 'docu', 'font', 'font-family'] + self.parent = parent + + + macro_list = ['docu', 'font', 'font-family','font-size','underline','bold','italic'] + boolean_list = ['True', 'False'] + operator_list = [ '-', '+', '*', '/', '>' ,'=','<','>=','<='] + # SILE and SILE-STRING-ADD! is internal, so they're not added. + function_list = ['if', 'docu', 'docu-para', 'script', 'call','xml-to-string', 'begin', + 'str','str-append','set!','print', 'define', 'def-syntax', 'lambda', 'eval'] + self.PRIMARY = macro_list + boolean_list + operator_list + function_list self.split_pattern = re.compile(r'(\s+|\\%|%|\\\[|\\\]|[[]|[]])') @@ -106,7 +114,7 @@ class ClochurLexer(QsciLexerCustom): self.startStyling(start, 0x1f) - rainbow_state = 0 + rainbow_state = 0 # 0~6 = Rainbowmode ; 0~6 + 10 (i.e. 10~16) = Rainbowmode with string index = SCI(QsciScintilla.SCI_LINEFROMPOSITION, start) @@ -128,7 +136,7 @@ class ClochurLexer(QsciLexerCustom): #print(line_utf8_splitted_len_pair) is_comment = False - is_string = False + next_is_defined_var = False i = 0 if index > 0: @@ -140,12 +148,37 @@ class ClochurLexer(QsciLexerCustom): for item in line_utf8_splitted_len_pair: + ## add to complete list + #if item["str"] in [ "define", "def-syntax"]: + # next_is_defined_var = True + #elif next_is_defined_var == True and not (item["str"] in ["[","]"]): + # print(next_is_defined_var,item["str"]) + # self.parent.append_autocompletion_item(item["str"]) + # next_is_defined_var = False + #else: + # pass '''comment''' if item["str"] == "%": is_comment = True if is_comment == True: new_state = self.Comment # end of comment + + # string + elif re.match(tmp_parser.string_pattern ,item["str"]): + new_state = self.String + elif re.match(r"[\"]([^\"\\]|[\\][\"nt]|[\\][\\])+?", item["str"]): + rainbow_state += 10 + new_state = self.String + elif (re.match(r"([^\"\\]|[\\][\"nt]|[\\][\\])+?[\"]" ,item["str"]) or re.match(r'["]' ,item["str"])): + new_state = self.String + rainbow_state -= 10 + + elif item["str"] == "]" and rainbow_state >= 10: + new_state = self.String + elif rainbow_state >= 10: + new_state = self.String + elif item["str"] in self.PRIMARY: # keywords new_state = self.Keyword @@ -154,26 +187,25 @@ class ClochurLexer(QsciLexerCustom): new_state = self.Number elif re.match(tmp_parser.float_pattern, item["str"]): new_state = self.Number - - # string - elif re.match(tmp_parser.string_pattern ,item["str"]): - new_state = self.String - elif re.match(r"[\"]([^\"\\]|[\\][\"\n\t]|[\\])*?", item["str"]): - is_string = True - new_state = self.String - elif re.match(r"([^\"\\]|[\\][\"\n\t]|[\\])*?[\"]" ,item["str"]): - new_state = self.String - is_string = False - elif is_string == True: - new_state = self.String #parenthesis: rainbow mode elif item["str"] == "[": - new_state = getattr(self, "Rainbow" + str(rainbow_state)) - rainbow_state = (rainbow_state + 1) % 7 + if rainbow_state >= 10: + new_state = self.String + elif rainbow_state < 7: + print("rainbow_state" + str(type(rainbow_state)) + str(rainbow_state)) + new_state = getattr(self, "Rainbow" + str(rainbow_state)) + rainbow_state = (rainbow_state + 1) % 7 + else: + pass elif item["str"] == "]": - rainbow_state = (rainbow_state - 1) % 7 - new_state = getattr(self, "Rainbow" + str(rainbow_state)) + if rainbow_state >= 10: + new_state = self.String + elif rainbow_state < 7: + rainbow_state = (rainbow_state - 1) % 7 + new_state = getattr(self, "Rainbow" + str(rainbow_state)) + else: + pass else: pass diff --git a/src/Editor/CustomQsciEditor.py b/src/Clochur/CustomQsciEditor.py similarity index 59% rename from src/Editor/CustomQsciEditor.py rename to src/Clochur/CustomQsciEditor.py index 66d731d..5a6f635 100644 --- a/src/Editor/CustomQsciEditor.py +++ b/src/Clochur/CustomQsciEditor.py @@ -2,9 +2,9 @@ #-*-coding:utf-8-*- from PyQt5.QtGui import * -from PyQt5.Qsci import QsciScintilla +from PyQt5.Qsci import QsciScintilla, QsciAPIs -from Editor.ClochurLexer import ClochurLexer +from Clochur.ClochurLexer import ClochurLexer class CustomQsciEditor(QsciScintilla): def __init__(self, parent=None): @@ -15,8 +15,7 @@ class CustomQsciEditor(QsciScintilla): self.tab_width = 4 - lexer = ClochurLexer(self) - self.setLexer(lexer) + # Margin 0 for line numbers font = QFont() @@ -51,6 +50,39 @@ class CustomQsciEditor(QsciScintilla): self.setTabWidth(4) self.setBackspaceUnindents(True) + # set auto complete + + #set lexer + self.lexer = ClochurLexer(self) + self.auto_complete_api = QsciAPIs(self.lexer) # autocomplete function + self.setLexer(self.lexer) + + self.setAutoCompletionCaseSensitivity(True) + + self.setAutoCompletionReplaceWord(False) + + # Use the predefined APIs as the source. + self.setAutoCompletionSource(QsciScintilla.AcsAll) + + + + # after 1 character, show completetion + self.setAutoCompletionThreshold(1) + + # add autocompletion items + autocompletions = self.lexer.PRIMARY + + + for ac in autocompletions: + self.auto_complete_api.add(ac) + + # "prepare" the QsciAPIs-object: + self.auto_complete_api.prepare() + + def append_autocompletion_item(self, item): + self.auto_complete_api.add(item) + self.auto_complete_api.prepare() + def set_word_wrap(self): self.setWrapMode(QsciScintilla.WrapMode.WrapWord) def set_no_word_wrap(self): diff --git a/src/Editor/FindReplace.py b/src/Clochur/FindReplace.py similarity index 100% rename from src/Editor/FindReplace.py rename to src/Clochur/FindReplace.py diff --git a/src/Editor/Interpreter.py b/src/Clochur/Interpreter.py similarity index 76% rename from src/Editor/Interpreter.py rename to src/Clochur/Interpreter.py index 03587c1..b8bf1b1 100644 --- a/src/Editor/Interpreter.py +++ b/src/Clochur/Interpreter.py @@ -2,7 +2,8 @@ import re import xml.etree.ElementTree as ET -from Editor.Parser import Parser +import itertools +from Clochur.Parser import Parser ''' macro expansion for example: @@ -21,6 +22,11 @@ class Interpreter: self.macro_list = dict() self.silexml = ET.Element('sile') + #self.editor = None + + #if "editor" in kwargs.keys(): + # self.editor = kwargs["editor"] + self.preprocessing_commands = '''[def-syntax docu [[_ x] [SILE[docu-aux x]]] [[_ x y...] [SILE[docu-aux x y...]]]] @@ -103,9 +109,11 @@ class Interpreter: string = str(string) if re.match(string_pattern, string): # reverse the escape characters - print(string) - string = re.sub(r'\\"(.+)',r'"\1',string) - print(string) + while True: + string_before = string + string = re.sub(r'\\"(.+)',r'"\1',string) + if string_before == string: + break return string[1:-1] else: return string @@ -153,10 +161,26 @@ class Interpreter: else: return sexp["token"] + + elif isinstance(sexp, Lambda): + return sexp + + # lambda apply + elif isinstance(sexp[0], Lambda): + return self.apply(sexp) + + # count sexp[0] first. + elif isinstance(sexp[0], list): + new_sexp_0 = self.interprete_aux(sexp[0]) + new_sexp = [new_sexp_0] + sexp[1:] + return self.interprete_aux(new_sexp) + + elif sexp[0]["token"] in ["+", "-", "*", "/", "<", "=", ">", "<=", ">="]: if len(sexp) != 3: + parser = Parser() raise Exception("Ln %d, Col %d: the argument length %d of %s is not correct." % - (sexp[0]["line"], sexp[0]["col"], len(sexp), a.generate_printable_sexp(sexp))) + (sexp[0]["line"], sexp[0]["col"], len(sexp), parser.generate_printable_sexp(sexp))) else: if sexp[0]["token"] == "+": return self.interprete_aux(sexp[1]) + self.interprete_aux(sexp[2]) @@ -203,7 +227,8 @@ class Interpreter: pass if not is_found: - raise Exception("The syntax pattern for %s is not found." % a.generate_printable_sexp(sexp)) + parser = Parser() + raise Exception("The syntax pattern for %s is not found." % parser.generate_printable_sexp(sexp)) else: new_sexp = self.macro_expand(syntax_after, unification) @@ -216,8 +241,14 @@ class Interpreter: if sexp[1]["type"] != "sym": raise Exception("Ln %d, Col %d: the type of %s should be symbol, not %s" % (sexp[1]["line"], sexp[1]["col"], sexp[1], sexp[1]["type"])) - else: - self.env[-1][sexp[1]["token"]] = self.interprete_aux(sexp[2]) + else: + defined_var = sexp[1]["token"] + self.env[-1][defined_var] = self.interprete_aux(sexp[2]) + + #if self.editor != None: + # self.editor.append_autocompletion_item(defined_var) + return "" + elif sexp[0]["token"] == "if": if len(sexp) != 4: raise Exception("Ln %d, Col %d: the number of argument of if should be 3." % @@ -233,7 +264,12 @@ class Interpreter: raise Exception("Ln %d, Col %d: the argument number of str should be 1" % (sexp[0]["line"], sexp[0]["col"])) else: - if isinstance(sexp[1], dict) and (not (sexp[1]["token"] in self.macro_list.keys())): + vars_frame = [frame.keys() for frame in self.env] + vars_list = list(itertools.chain.from_iterable(vars_frame)) + macro_vars = self.macro_list.keys() + if isinstance(sexp[1], dict) and \ + (not (sexp[1]["token"] in macro_vars)) and \ + (not (sexp[1]["token"] in vars_list)) : return str(self.destring(sexp[1]["token"])) else: return str(self.destring(self.interprete_aux(sexp[1]))) @@ -253,6 +289,14 @@ class Interpreter: result = self.interprete_aux(sexp[1]) print(result) return "" + elif sexp[0]["token"] == 'eval': + if len(sexp) != 2: + raise Exception("Ln %d, Col %d: the argument number of eval should be 1" % + (sexp[0]["line"], sexp[0]["col"])) + else: + result = self.interprete_aux(sexp[1]) + return result + elif sexp[0]["token"] == "set!": if sexp[1]["type"] != "sym": raise Exception("Ln %d, Col %d: the type of %s should be symbol, not %s" % @@ -268,6 +312,8 @@ class Interpreter: if not is_found: raise Exception("Ln %d, Col %d: the variable %s is not found!" % (sexp[1]["line"], sexp[1]["col"], sexp[1]["token"])) + + return "" elif sexp[0]["token"] == "def-syntax": @@ -289,6 +335,10 @@ class Interpreter: self.macro_list[syntax_name] = result_list + # add to auto completion list + #if self.editor != None: + # self.editor.append_autocompletion_item(defined_var) + return "" elif sexp[0]["token"] == "begin": @@ -351,6 +401,58 @@ class Interpreter: else: raise Exception("Line %d, Col. %d, the form of call is mal-formed." % (sexp[0]["line"], sexp[0]["col"])) + # make a List class object + elif sexp[0]["token"] == "ls": + result = [] + + if len(sexp) == 1: + return result + else: + result = [self.interprete_aux(x) for x in sexp[1:]] + + return List(result) + + # (car List) + elif sexp[0]["token"] == "car": + if len(sexp) != 2: + raise Exception("Line %d, Col. %d, the argument length should be 1" % (sexp[0]["line"], sexp[0]["col"])) + elif not isinstance(sexp[1], List): + raise Exception("Line %d, Col. %d, the argument is not a list." % (sexp[1]["line"], sexp[1]["col"])) + else: + ls = sexp[1].ls + return ls[0] + + # (cdr List) + elif sexp[0]["token"] == "cdr": + if len(sexp) != 2: + raise Exception("Line %d, Col. %d, the argument length should be 1" % (sexp[0]["line"], sexp[0]["col"])) + elif not isinstance(sexp[1], List): + raise Exception("Line %d, Col. %d, the argument is not a list." % (sexp[1]["line"], sexp[1]["col"])) + else: + ls = sexp[1].ls + return ls[1:] + + # (cons any List) + elif sexp[0]["token"] == "cons": + if len(sexp) != 3: + raise Exception("Line %d, Col. %d, the argument length should be 2" % (sexp[0]["line"], sexp[0]["col"])) + elif not isinstance(sexp[2], List): + raise Exception("Line %d, Col. %d, the 2nd argument of cons is not a list." % (sexp[2]["line"], sexp[2]["col"])) + else: + car = sexp[1] + cdr = sexp[2].ls + result_ls = List([sexp[1]]+cdr) + return result_ls + + + elif sexp[0]["token"] == "ls-ref": + if len(sexp) != 3: + raise Exception("Line %d, Col. %d, the argument length should be 1" % (sexp[0]["line"], sexp[0]["col"])) + elif not isinstance(sexp[1], List): + raise Exception("Line %d, Col. %d, the 2nd argument of cons is not a list." % (sexp[2]["line"], sexp[2]["col"])) + + + # if it's a sub-xml-element, show the string form of it, or return the input unchanged. # It's recommended to use it only print it in terminal with 'print' elif sexp[0]["token"] == "xml-to-string": @@ -384,29 +486,34 @@ class Interpreter: return ET.tostring(self.silexml, encoding="unicode") else: - sexp_new = [self.interprete_aux(x) for x in sexp] + ret = self.apply(sexp) + return ret + + def apply(self, sexp): - if isinstance(sexp_new[0], Lambda): - vars = sexp_new[0].vars - env = sexp_new[0].env - body = sexp_new[0].body - vars_input = sexp_new[1:] + sexp_new = [self.interprete_aux(x) for x in sexp] + + if isinstance(sexp_new[0], Lambda): + vars = sexp_new[0].vars + env = sexp_new[0].env + body = sexp_new[0].body + vars_input = sexp_new[1:] - if len(vars) != len(vars_input): - raise Exception("Ln %d, Col %d: argument length is not matched." % - (self.get_first_item(sexp[0])["line"], self.get_first_item(sexp[0])["col"])) + if len(vars) != len(vars_input): + raise Exception("Ln %d, Col %d: argument length is not matched." % + (self.get_first_item(sexp[0])["line"], self.get_first_item(sexp[0])["col"])) - new_env_block = dict() - for i in range(len(vars)): - new_env_block[vars[i]] = vars_input[i] + new_env_block = dict() + for i in range(len(vars)): + new_env_block[vars[i]] = vars_input[i] - new_env = [new_env_block] + env + new_env = [new_env_block] + env - old_env = self.env - self.env = new_env - ret = self.interprete_aux(body) - self.env = old_env - return ret + old_env = self.env + self.env = new_env + ret = self.interprete_aux(body) + self.env = old_env + return ret def remove_dict_form(self, sexp): @@ -426,7 +533,7 @@ class Interpreter: if isinstance(before_stx[i], list): unification = self.unify(sexp[i], before_stx[i], unification) elif before_stx[i]["token"] in unification.keys(): - raise Exception("the variable %s is double defined." % before-stx[i]) + raise Exception("the variable %s is double defined." % before_stx[i]) elif re.match(r".+[.]{3}$", before_stx[i]["token"]): if i == len(before_stx) - 1: unification[before_stx[i]["token"]] = {"content": sexp[i:], "dotted":True} @@ -467,8 +574,14 @@ class SubXMLElement: def __init__(self, element): self.element = element - def __str__(init): + def __str__(self): return "" + +# closure +class List: + def __init__(self, ls): + self.ls = ls + # closure class Lambda: def __init__(self, vars, body, env): diff --git a/src/Editor/Parser.py b/src/Clochur/Parser.py similarity index 97% rename from src/Editor/Parser.py rename to src/Clochur/Parser.py index d19a8d2..e8ea0fb 100644 --- a/src/Editor/Parser.py +++ b/src/Clochur/Parser.py @@ -6,7 +6,7 @@ class Parser(): bool_pattern = r"(?PTrue|False)" int_pattern =r"(?P[+-]?\d+)" symbol_pattern = r"(?P[_a-zA-Z][-!:._0-9a-zA-Z]*)" - string_pattern = r"(?P[\"]([^\"\\]|[\\][\\\"\n\t]|[\\])*?[\"])" + string_pattern = r"(?P[\"]([^\"\\]|[\\][\\\"nt]|[\\])*?[\"])" parenthesis_pattern = r"(?P[[]|[]])" percent_pattern = r"(?P[%])" space_pattern = r"(?P[ \t]+)" diff --git a/src/Editor/__about__.py b/src/Clochur/__about__.py similarity index 100% rename from src/Editor/__about__.py rename to src/Clochur/__about__.py diff --git a/src/Editor/__init__.py b/src/Clochur/__init__.py similarity index 93% rename from src/Editor/__init__.py rename to src/Clochur/__init__.py index 3c64b30..d90a9d9 100755 --- a/src/Editor/__init__.py +++ b/src/Clochur/__init__.py @@ -14,13 +14,17 @@ from PyQt5 import QtWebEngineWidgets from PyQt5.QtWidgets import * from PyQt5.Qsci import QsciScintilla -from Editor import qrc_resources +src_dirname = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) +sys.path.append(src_dirname) -from Editor import __about__ -from Editor import FindReplace -from Editor.Interpreter import Interpreter, Lambda -from Editorimport CustomQsciEditor -from Editor.Parser import Parser + +from Clochur import qrc_resources + +from Clochur import __about__ +from Clochur import FindReplace +from Clochur.Interpreter import Interpreter, Lambda +from Clochur import CustomQsciEditor +from Clochur.Parser import Parser sile_command = 'sile' @@ -175,7 +179,6 @@ class Window(QMainWindow): format_menu = menuBar.addMenu("&Format") format_menu.addAction(self.bold_action) format_menu.addAction(self.italic_action) - format_menu.addAction(self.strike_action) format_menu.addAction(self.underline_action) help_menu = menuBar.addMenu("&Help") @@ -195,7 +198,11 @@ class Window(QMainWindow): self.opened_file_dirname = os.path.dirname(file_path[0]) self.file = open(file_path[0], 'r', encoding='utf-8') self.editor.setText(self.file.read()) + self.setWindowTitle("Clochur - %s" % self.filename) + self.editor.setModified(False) self.file.close() + else: + pass @@ -207,7 +214,7 @@ class Window(QMainWindow): else: self.file = open(os.path.join(self.opened_file_dirname,self.filename), 'w', encoding='utf-8') - file_content = editor.text() + file_content = self.editor.text() self.file.write(file_content) self.file.close() @@ -218,6 +225,7 @@ class Window(QMainWindow): with open(os.path.join(self.tmp_folder, self.tmp_file), 'r') as f: data = json.load(f) data["untitled"].remove(self.untitled_id) + self.untitled_id = None with open(os.path.join(self.tmp_folder, self.tmp_file), 'w') as f: json.dump(data, f, indent=4) @@ -286,13 +294,17 @@ class Window(QMainWindow): if self.editor.isModified(): reply = QMessageBox.question(self,'','Do You want to save this file? The text has been modified', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.No) if reply == QMessageBox.Yes: - file_path = QFileDialog.getSaveFileName(self, 'Save file as...', self.opened_file_dirname, "CLC typesetting format (*.clc)") - if file_path[0] != '': - self.file = open(file_path[0], 'w', encoding='utf-8') - file_content = editor.text() - self.file.write(file_content) - self.file.close() - self.removing_untitled_id() + if self.filename != None: + self.save_call() + else: + + file_path = QFileDialog.getSaveFileName(self, 'Save file as...', self.opened_file_dirname, "CLC typesetting format (*.clc)") + if file_path[0] != '': + self.file = open(file_path[0], 'w', encoding='utf-8') + file_content = self.editor.text() + self.file.write(file_content) + self.file.close() + self.removing_untitled_id() elif reply == QMessageBox.No: self.removing_untitled_id() @@ -378,7 +390,6 @@ class Window(QMainWindow): formatToolBar.addAction(self.bold_action) formatToolBar.addAction(self.italic_action) - formatToolBar.addAction(self.strike_action) formatToolBar.addAction(self.underline_action) '''create font adder''' diff --git a/src/Editor/qrc_resources.py b/src/Clochur/qrc_resources.py similarity index 100% rename from src/Editor/qrc_resources.py rename to src/Clochur/qrc_resources.py diff --git a/src/Editor/resources.qrc b/src/Clochur/resources.qrc similarity index 100% rename from src/Editor/resources.qrc rename to src/Clochur/resources.qrc diff --git a/src/example.pdf b/src/example.pdf index cd43dc3..1fd5496 100644 Binary files a/src/example.pdf and b/src/example.pdf differ