Here is something I am working on


#1

I am currently working on creating a text editor in Python. It is currently extremely early in development, but I hope to improve it with time. It can currently open files (three languages support syntax highlighting via the JSON theme, it will occasionally crash if you try to open a language that does not have a lexer set up for it yet, because I haven’t done any error handling yet). My goals with this project are to make a simple code editor that is easy to expand on, and that is not as resource intensive as Atom or VS Code are.

To be clear I am not trying to make an IDE, or even a powerful code editor. My goal is just to make something that works for quick editing of files, etc.

Current Features:

  • Syntax highlighting for Python, JSON, CSS, and Markdown, which can be modified via a JSON theme file
  • You can open and save files, still working on a tab view to have multiple files open at once. If anyone could help me figure out how to use tabs in this context that would be greatly appreciated.

Screenshot


#2

Hey @swissChili that’s looking really slick :slight_smile:

Can you share how you god the syntax highlighting working? I built a very basic editor in Qt that could syntax highlight Python, but only Python, and kind of badly. Be really interested to see how you’ve tackled it.


#3

Well, QScintilla is doing pretty much all the heavy lifting. All I’ve implemented is the theming. You can see the repo here if you want see all the code, but the themes for syntax highlighting work like this:

#this dict defines which file names match which Lexers:
lexers = {
"py": QsciLexerPython,
"css": QsciLexerCSS,
"cs": QsciLexerCSharp,
"coffee": QsciLexerCoffeeScript,
"json": QsciLexerJSON,
"html": QsciLexerHTML,
"yml": QsciLexerYAML,
"md": QsciLexerMarkdown,
} # not indented properly because markdown is finnicky

Then, I call this each time I open a different file:

          # some basic regex i'm using to get the file extension
           regex = r".+\.(\w+)"
            result = re.search(regex, path)
            print(result[1])
            l = result[1]
            # I use this to set a variable to the method for the appropriate lexer, which is read from the dict I defined earlier
            lexer = lexers[l]()

            # self.__theme is defined during the setup of the program, and read from a JSON file
            languageTheme = self.__theme[l]

            for name, value in languageTheme.items():
                    print(value, name)
                    # this loops through all the values of the lexer and sets their color to the color defined for that type in the theme
                    lexer.setColor(QColor(value), getattr(lexers[l](), name))
                    lexer.setPaper(self.__bgcolor, getattr(lexers[l](), name))
                    lexer.setFont(self.__myFont, getattr(lexers[l](), name))

            lexer.setDefaultFont(self.__myFont)
            lexer.setDefaultPaper(self.__bgcolor)
            lexer.setDefaultColor(self.__fgcolor)

            self.__editor.setLexer(lexer) # here I actually set the editors syntax highlighting

I hope that made sense, if not, please reply and I’ll be glad to clarify. My code is really messy at the moment, but I hope to clean it up soon.

Also I am not sure if what I did with the dict with string keys, and method values is valid, but it works, and I don’t have a better way to do it. I’d appreciate if you could clarify if that is a valid way of doing things.


#4

Nice, QScintilla looks like a really nice solution for this, you get a lot for ‘free’.

For the file-extension detection you might want to look at os.path.splitext in the standard library. If you pass it a file it will return a tuple of file basename and extensions, e.g.

import os
fn = 'this_is_my_file.py"
basename, ext = os.path.splitext(fn)
# basename = 'this_is_my_file'
# ext = '.py'

Note that the extension includes the .

What you did with the dictionary of lexers is great — this is the usual way to implement a sort of “switch” in Python like this. What you might want to do is add a default when you get, and skip the initialisation if there is no matching lexer, e.g.

lexer = lexers.get(l, None)
if lexer:
        lexer = lexer() # Initialize

You can do it in a single line by using a function/lambda that returns None as the default but its getting a little less readable.

lexer = lexers.get(l, lambda: None)()
if lexer:
      ...

You can then use this if lexer block to skip the lexer setup and self.__editor.setLexer(lexer) for non-lexable files.