# HG changeset patch # User windel # Date 1349622827 -7200 # Node ID 4a27c28c7d0f4be36b6e80609a5e9d3420f7991d # Parent 32078200cdd6b8c72636b52429175b34cadc148c File movage diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/build.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/apps/build.py Sun Oct 07 17:13:47 2012 +0200 @@ -0,0 +1,18 @@ +import sys, os + +sys.path.insert(0, os.path.join('..','libs')) +print(sys.path) +# Compiler imports: +from compiler.compiler import Compiler +from project import Project + +if __name__ == '__main__': + if len(sys.argv) < 2: + print('Use {0} projectfile'.format(sys.argv[0])) + sys.exit(-1) + filename = sys.argv[1] + project = Project() + project.load(filename) + pc = Compiler() + pc.compileProject(project) + diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/ide.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/apps/ide.py Sun Oct 07 17:13:47 2012 +0200 @@ -0,0 +1,322 @@ +import sys + +from PyQt4.QtCore import * +from PyQt4.QtGui import * +# ide components: +from .codeeditor import CodeEdit +from .astviewer import AstViewer +import base64 +import os.path + +# Compiler imports: +from project import Project +from compiler.compiler import Compiler + +lcfospng = base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEhMKBk7B678AAAA/SURBVFjD\n7dbBCQAgDATBi9h/y7EFA4Kf2QLCwH1S6XQu6sqoujublc8BAAAAAAAAAAB8B+zXT6YJAAAAAKYd\nWSgFQNUyijIAAAAASUVORK5CYII=\n') + +class BuildOutput(QTextEdit): + """ Build output component """ + def __init__(self, parent=None): + super(BuildOutput, self).__init__(parent) + self.setCurrentFont(QFont('Courier')) + self.setReadOnly(True) + self.append('Build output will appear here!') + +class BuildErrors(QListView): + sigErrorSelected = pyqtSignal(object) + def __init__(self, parent=None): + super(BuildErrors, self).__init__(parent) + model = QStandardItemModel() + self.setModel(model) + self.clicked.connect(self.itemSelected) + def setErrorList(self, errorlist): + model = QStandardItemModel() + for e in errorlist: + row, col, msg = e + item = QStandardItem(str(msg)) + item.setData(e) + model.appendRow(item) + self.setModel(model) + def itemSelected(self, index): + if not index.isValid(): + return + model = self.model() + item = model.itemFromIndex(index) + err = item.data() + self.sigErrorSelected.emit(err) + +class ProjectView(QWidget): + sigLoadFile = pyqtSignal(str) + def __init__(self, parent=None): + super(ProjectView, self).__init__(parent) + self.treeview = QTreeView(self) + self.treeview.setContextMenuPolicy(Qt.CustomContextMenu) + l = QVBoxLayout(self) + l.addWidget(self.treeview) + pm = QPixmap() + pm.loadFromData(lcfospng) + self.projectIcon = QIcon(pm) + # Connect signals: + self.treeview.activated.connect(self.activate) + self.treeview.customContextMenuRequested.connect(self.contextMenu) + def setProject(self, project): + self.project = project + model = QStandardItemModel() + root = model.invisibleRootItem() + pitem = QStandardItem(self.projectIcon, project.name) + pitem.setEditable(False) + pitem.setData(project) + root.appendRow(pitem) + for f in self.project.files: + fitem = QStandardItem(f) + pitem.appendRow(fitem) + fitem.setEditable(False) + fitem.setData(f) + self.treeview.setModel(model) + self.treeview.expandAll() + def contextMenu(self, pos): + idx = self.treeview.indexAt(pos) + if not idx.isValid(): + return + item = self.treeview.model().itemFromIndex(idx) + def activate(self, index): + if not index.isValid(): + return + model = self.treeview.model() + item = model.itemFromIndex(index) + fn = item.data() + if type(fn) is str: + self.sigLoadFile.emit(fn) + +class AboutDialog(QDialog): + def __init__(self, parent=None): + super(AboutDialog, self).__init__(parent) + self.setWindowTitle('About') + l = QVBoxLayout(self) + txt = QTextEdit(self) + txt.setReadOnly(True) + aboutText = """

lcfOS IDE

+

An all-in-one IDE for OS development.

+

https://www.assembla.com/spaces/lcfOS/wiki

+

Author: Windel Bouwman

+ """ + txt.append(aboutText) + l.addWidget(txt) + but = QPushButton('OK') + but.setDefault(True) + but.clicked.connect(self.close) + l.addWidget(but) + +class ProjectOptions(QDialog): + pass + # TODO: project options in here + +class Ide(QMainWindow): + def __init__(self, parent=None): + super(Ide, self).__init__(parent) + self.setWindowTitle('LCFOS IDE') + icon = QPixmap() + icon.loadFromData(lcfospng) + self.setWindowIcon(QIcon(icon)) + + # Create menus: + self.fileMenu = self.menuBar().addMenu('File') + self.viewMenu = self.menuBar().addMenu('View') + self.projectMenu = self.menuBar().addMenu('Project') + self.helpMenu = self.menuBar().addMenu('Help') + + # Create mdi area: + self.mdiArea = QMdiArea() + self.setCentralWidget(self.mdiArea) + + # Create components: + self.buildOutput = BuildOutput() + self.addComponent('Build output', self.buildOutput) + + self.astViewer = AstViewer() + self.addComponent('AST viewer', self.astViewer) + self.astViewer.sigNodeSelected.connect(self.nodeSelected) + + self.builderrors = BuildErrors() + self.addComponent('Build errors', self.builderrors) + self.builderrors.sigErrorSelected.connect(self.errorSelected) + + self.projectview = ProjectView() + self.addComponent('Project', self.projectview) + self.projectview.sigLoadFile.connect(self.loadFile) + + # About dialog: + self.aboutDialog = AboutDialog() + self.aboutDialog.setWindowIcon(QIcon(icon)) + # Create actions: + self.buildAction = QAction('Build!', self) + self.buildAction.setShortcut(QKeySequence('F7')) + self.projectMenu.addAction(self.buildAction) + self.buildAction.triggered.connect(self.buildFile) + self.openProjectAction = QAction("Open project", self) + self.openProjectAction.triggered.connect(self.openProject) + self.projectMenu.addAction(self.openProjectAction) + self.helpAction = QAction('Help', self) + self.helpAction.setShortcut(QKeySequence('F1')) + self.helpMenu.addAction(self.helpAction) + self.aboutAction = QAction('About', self) + self.helpMenu.addAction(self.aboutAction) + self.aboutAction.triggered.connect(self.aboutDialog.open) + + self.newFileAction = QAction("New File", self) + self.fileMenu.addAction(self.newFileAction) + self.newFileAction.triggered.connect(self.newFile) + self.saveFileAction = QAction("Save File", self) + self.fileMenu.addAction(self.saveFileAction) + self.saveFileAction.triggered.connect(self.saveFile) + self.closeFileAction = QAction("Close File", self) + self.fileMenu.addAction(self.closeFileAction) + self.closeFileAction.triggered.connect(self.closeFile) + + cascadeAction = QAction("Cascade windows", self) + cascadeAction.triggered.connect(self.mdiArea.cascadeSubWindows) + self.viewMenu.addAction(cascadeAction) + tileAction = QAction('Tile windows', self) + tileAction.triggered.connect(self.mdiArea.tileSubWindows) + self.viewMenu.addAction(tileAction) + + # Load settings: + self.settings = QSettings('windelsoft', 'lcfoside') + self.loadSettings() + + def addComponent(self, name, widget): + dw = QDockWidget(name) + dw.setWidget(widget) + dw.setObjectName(name) + self.addDockWidget(Qt.RightDockWidgetArea, dw) + self.viewMenu.addAction(dw.toggleViewAction()) + + # File handling: + def newFile(self): + ce = CodeEdit() + w = self.mdiArea.addSubWindow(ce) + ce.show() + + def saveFile(self): + ac = self.activeMdiChild() + if ac: + ac.saveFile() + + def saveAll(self): + pass + + def openFile(self): + # TODO + pass + + def closeFile(self): + ac = self.activeMdiChild() + if ac: + self.mdiArea.removeSubWindow(ac) + + def loadFile(self, filename): + # Find existing mdi widget: + wid = self.findMdiChild(filename) + if wid: + self.mdiArea.setActiveSubWindow(wid.parent()) + return wid + + # Create a new one: + ce = CodeEdit() + source = self.project.loadProjectFile(filename) + ce.setSource(source) + self.mdiArea.addSubWindow(ce) + ce.show() + return ce + + # MDI: + def activeMdiChild(self): + aw = self.mdiArea.activeSubWindow() + if aw: + return aw.widget() + else: + return None + + def findMdiChild(self, filename): + for window in self.mdiArea.subWindowList(): + wid = window.widget() + if wid.filename == filename: + return wid + return None + + def allChildren(self): + c = [] + for window in self.mdiArea.subWindowList(): + wid = window.widget() + c.append(wid) + return c + + # Settings: + def loadSettings(self): + if self.settings.contains('mainwindowstate'): + self.restoreState(self.settings.value('mainwindowstate')) + if self.settings.contains('mainwindowgeometry'): + self.restoreGeometry(self.settings.value('mainwindowgeometry')) + if self.settings.contains('openedproject'): + projectfile = self.settings.value('openedproject') + self.loadProject(projectfile) + + def closeEvent(self, ev): + self.settings.setValue('mainwindowstate', self.saveState()) + self.settings.setValue('mainwindowgeometry', self.saveGeometry()) + if self.project: + self.settings.setValue('openedproject', self.project.filename) + # TODO: ask for save of opened files + ev.accept() + + # Error handling: + def nodeSelected(self, node): + ce = self.activeMdiChild() + if not ce: + return + if node.location: + row, col = node.location + ce.highlightErrorLocation( row, col ) + else: + ce.clearErrors() + + def errorSelected(self, err): + row, col, msg = err + ce = self.activeMdiChild() + if not ce: + return + ce.highlightErrorLocation(row, col) + + # Project loading: + def loadProject(self, filename): + self.project = Project(filename) + self.projectview.setProject(self.project) + + def openProject(self): + filename = QFileDialog.getOpenFileName(self, \ + "Choose project file", "", "lcfos Project files (*.lcp)") + if filename: + self.loadProject(filename) + + # Build recepy: + def buildFile(self): + """ Build project """ + self.saveAll() + self.buildOutput.clear() + self.buildOutput.append(str(self.compiler)) + mods = self.compiler.compileProject(self.project) + + self.builderrors.setErrorList(self.compiler.errorlist) + self.astViewer.setAst(mods[0]) + for err in self.compiler.errorlist: + self.buildOutput.append(str(err)) + self.buildOutput.append("Done!") + + +if __name__ == '__main__': + app = QApplication(sys.argv) + ide = Ide() + ide.compiler = Compiler() + ide.show() + app.exec_() + diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/ide/__init__.py --- a/python/apps/ide/__init__.py Sun Oct 07 17:04:10 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -# Package - diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/ide/astviewer.py --- a/python/apps/ide/astviewer.py Sun Oct 07 17:04:10 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -from PyQt4.QtCore import * -from PyQt4.QtGui import * - -def astToNamedElement(astNode, parentNode): - """ Helper to convert and AST tree to NamedElement tree: """ - item = QStandardItem(str(astNode)) - item.setData(astNode) - parentNode.appendRow(item) - for c in astNode.getChildren(): - astToNamedElement(c, item) - -# The actual widget: -class AstViewer(QTreeView): - sigNodeSelected = pyqtSignal(object) - def __init__(self, parent=None): - super(AstViewer, self).__init__(parent) - self.setHeaderHidden(True) - self.clicked.connect(self.selectHandler) - - def setAst(self, ast): - """ Create a new model and add all ast elements to it """ - model = QStandardItemModel() - if ast: - astToNamedElement(ast, model.invisibleRootItem()) - self.setModel( model ) - self.expandAll() - - def selectHandler(self, index): - if not index.isValid(): - return - model = self.model() - item = model.itemFromIndex(index) - node = item.data() - self.sigNodeSelected.emit(node) - - diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/ide/codeeditor.py --- a/python/apps/ide/codeeditor.py Sun Oct 07 17:04:10 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ -from PyQt4.QtCore import * -from PyQt4.QtGui import * -import compiler.lexer -import os.path - -class MySyntaxHighlighter(QSyntaxHighlighter): - def __init__(self, parent=None): - super(MySyntaxHighlighter, self).__init__(parent) - # Syntax highlighting: - self.rules = [] - fmt = QTextCharFormat() - fmt.setForeground(Qt.darkBlue) - fmt.setFontWeight(QFont.Bold) - for kw in compiler.lexer.keywords: - pattern = '\\b'+kw+'\\b' - self.rules.append( (pattern, fmt) ) - - # Comments: - fmt = QTextCharFormat() - fmt.setForeground(Qt.gray) - fmt.setFontItalic(True) - pattern = '\{.*\}' - self.rules.append( (pattern, fmt) ) - - # Procedure: - fmt = QTextCharFormat() - fmt.setForeground(Qt.blue) - fmt.setFontItalic(True) - #pattern = '(?<=procedure )[A-Za-z]' - # TODO lookbehind does not work, think something else - #self.rules.append( (pattern, fmt) ) - - def highlightBlock(self, text): - for pattern, fmt in self.rules: - expression = QRegExp(pattern) - index = expression.indexIn(text) - while index >= 0: - length = expression.matchedLength() - self.setFormat(index, length, fmt) - index = expression.indexIn(text, index + length) - -class LineNumberArea(QWidget): - def __init__(self, codeedit): - super(LineNumberArea, self).__init__(codeedit) - self.codeedit = codeedit - # TODO: display error in this: self.setToolTip('hello world') - def sizeHint(self): - return QSize(self.codeedit.lineNumberAreaWidth(), 0) - def paintEvent(self, ev): - self.codeedit.lineNumberAreaPaintEvent(ev) - -class CodeEdit(QPlainTextEdit): - def __init__(self, parent=None): - super(CodeEdit, self).__init__(parent) - # members: - self.isUntitled = True - self.filename = None - self.setFont(QFont('Courier')) - self.lineNumberArea = LineNumberArea(self) - - self.blockCountChanged.connect(self.updateLineNumberAreaWidth) - self.updateRequest.connect(self.updateLineNumberArea) - - # Syntax highlighter: - self.highlighter = MySyntaxHighlighter(self.document()) - - def setFileName(self, filename): - self.filename = filename - self.isUntitled = False - self.setWindowTitle(filename) - def setSource(self, source): - self.setPlainText(source) - - def save(self): - pass - def saveAs(self): - pass - - def saveFile(self): - if self.isUntitled: - self.saveAs() - else: - source = str(self.toPlainText()) - f = open(self.filename, 'w') - f.write(source) - f.close() - - def highlightErrorLocation(self, row, col): - tc = QTextCursor(self.document()) - tc.clearSelection() - tc.movePosition(tc.Down, tc.MoveAnchor, row - 1) - tc.movePosition(tc.Right, tc.MoveAnchor, col - 1) - tc.movePosition(tc.NextCharacter, tc.KeepAnchor) # Select 1 character - selection = QTextEdit.ExtraSelection() - lineColor = QColor(Qt.red).lighter(160) - selection.format.setBackground(lineColor) - #selection.format.setProperty(QTextFormat.FullWidthSelection, True) - selection.cursor = tc - self.setExtraSelections( [ selection ] ) - def clearErrors(self): - self.setExtraSelections( [ ] ) - - def lineNumberAreaWidth(self): - digits = 1 - mx = max(1, self.blockCount()) - while mx >= 10: - mx = mx / 10 - digits += 1 - space = 3 + self.fontMetrics().width('8') * digits - return space - def lineNumberAreaPaintEvent(self, ev): - painter = QPainter(self.lineNumberArea) - painter.fillRect(ev.rect(), Qt.lightGray) - block = self.firstVisibleBlock() - blockNumber = block.blockNumber() - top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top() - bottom = top + self.blockBoundingRect(block).height() - while block.isValid() and top <= ev.rect().bottom(): - if block.isVisible() and bottom >= ev.rect().top(): - num = str(blockNumber + 1) - painter.setPen(Qt.black) - painter.drawText(0, top, self.lineNumberArea.width(), self.fontMetrics().height(), Qt.AlignRight, num) - block = block.next() - top = bottom - bottom = top + self.blockBoundingRect(block).height() - blockNumber += 1 - def resizeEvent(self, ev): - super(CodeEdit, self).resizeEvent(ev) - cr = self.contentsRect() - self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height() )) - def updateLineNumberAreaWidth(self, newBlockCount): - self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) - def updateLineNumberArea(self, rect, dy): - if dy > 0: - self.lineNumberArea.scroll(0, dy) - else: - self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) - if rect.contains(self.viewport().rect()): - self.updateLineNumberAreaWidth(0) - diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/ide/ide.py --- a/python/apps/ide/ide.py Sun Oct 07 17:04:10 2012 +0200 +++ b/python/apps/ide/ide.py Sun Oct 07 17:13:47 2012 +0200 @@ -1,309 +1,1 @@ -from PyQt4.QtCore import * -from PyQt4.QtGui import * -# ide components: -from .codeeditor import CodeEdit -from .astviewer import AstViewer -import base64 -from project import Project -import os.path -lcfospng = base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEhMKBk7B678AAAA/SURBVFjD\n7dbBCQAgDATBi9h/y7EFA4Kf2QLCwH1S6XQu6sqoujublc8BAAAAAAAAAAB8B+zXT6YJAAAAAKYd\nWSgFQNUyijIAAAAASUVORK5CYII=\n') - -class BuildOutput(QTextEdit): - """ Build output component """ - def __init__(self, parent=None): - super(BuildOutput, self).__init__(parent) - self.setCurrentFont(QFont('Courier')) - self.setReadOnly(True) - self.append('Build output will appear here!') - -class BuildErrors(QListView): - sigErrorSelected = pyqtSignal(object) - def __init__(self, parent=None): - super(BuildErrors, self).__init__(parent) - model = QStandardItemModel() - self.setModel(model) - self.clicked.connect(self.itemSelected) - def setErrorList(self, errorlist): - model = QStandardItemModel() - for e in errorlist: - row, col, msg = e - item = QStandardItem(str(msg)) - item.setData(e) - model.appendRow(item) - self.setModel(model) - def itemSelected(self, index): - if not index.isValid(): - return - model = self.model() - item = model.itemFromIndex(index) - err = item.data() - self.sigErrorSelected.emit(err) - -class ProjectView(QWidget): - sigLoadFile = pyqtSignal(str) - def __init__(self, parent=None): - super(ProjectView, self).__init__(parent) - self.treeview = QTreeView(self) - self.treeview.setContextMenuPolicy(Qt.CustomContextMenu) - l = QVBoxLayout(self) - l.addWidget(self.treeview) - pm = QPixmap() - pm.loadFromData(lcfospng) - self.projectIcon = QIcon(pm) - # Connect signals: - self.treeview.activated.connect(self.activate) - self.treeview.customContextMenuRequested.connect(self.contextMenu) - def setProject(self, project): - self.project = project - model = QStandardItemModel() - root = model.invisibleRootItem() - pitem = QStandardItem(self.projectIcon, project.name) - pitem.setEditable(False) - pitem.setData(project) - root.appendRow(pitem) - for f in self.project.files: - fitem = QStandardItem(f) - pitem.appendRow(fitem) - fitem.setEditable(False) - fitem.setData(f) - self.treeview.setModel(model) - self.treeview.expandAll() - def contextMenu(self, pos): - idx = self.treeview.indexAt(pos) - if not idx.isValid(): - return - item = self.treeview.model().itemFromIndex(idx) - def activate(self, index): - if not index.isValid(): - return - model = self.treeview.model() - item = model.itemFromIndex(index) - fn = item.data() - if type(fn) is str: - self.sigLoadFile.emit(fn) - -class AboutDialog(QDialog): - def __init__(self, parent=None): - super(AboutDialog, self).__init__(parent) - self.setWindowTitle('About') - l = QVBoxLayout(self) - txt = QTextEdit(self) - txt.setReadOnly(True) - aboutText = """

lcfOS IDE

-

An all-in-one IDE for OS development.

-

https://www.assembla.com/spaces/lcfOS/wiki

-

Author: Windel Bouwman

- """ - txt.append(aboutText) - l.addWidget(txt) - but = QPushButton('OK') - but.setDefault(True) - but.clicked.connect(self.close) - l.addWidget(but) - -class ProjectOptions(QDialog): - pass - # TODO: project options in here - -class Ide(QMainWindow): - def __init__(self, parent=None): - super(Ide, self).__init__(parent) - self.setWindowTitle('LCFOS IDE') - icon = QPixmap() - icon.loadFromData(lcfospng) - self.setWindowIcon(QIcon(icon)) - - # Create menus: - self.fileMenu = self.menuBar().addMenu('File') - self.viewMenu = self.menuBar().addMenu('View') - self.projectMenu = self.menuBar().addMenu('Project') - self.helpMenu = self.menuBar().addMenu('Help') - - # Create mdi area: - self.mdiArea = QMdiArea() - self.setCentralWidget(self.mdiArea) - - # Create components: - self.buildOutput = BuildOutput() - self.addComponent('Build output', self.buildOutput) - - self.astViewer = AstViewer() - self.addComponent('AST viewer', self.astViewer) - self.astViewer.sigNodeSelected.connect(self.nodeSelected) - - self.builderrors = BuildErrors() - self.addComponent('Build errors', self.builderrors) - self.builderrors.sigErrorSelected.connect(self.errorSelected) - - self.projectview = ProjectView() - self.addComponent('Project', self.projectview) - self.projectview.sigLoadFile.connect(self.loadFile) - - # About dialog: - self.aboutDialog = AboutDialog() - self.aboutDialog.setWindowIcon(QIcon(icon)) - # Create actions: - self.buildAction = QAction('Build!', self) - self.buildAction.setShortcut(QKeySequence('F7')) - self.projectMenu.addAction(self.buildAction) - self.buildAction.triggered.connect(self.buildFile) - self.openProjectAction = QAction("Open project", self) - self.openProjectAction.triggered.connect(self.openProject) - self.projectMenu.addAction(self.openProjectAction) - self.helpAction = QAction('Help', self) - self.helpAction.setShortcut(QKeySequence('F1')) - self.helpMenu.addAction(self.helpAction) - self.aboutAction = QAction('About', self) - self.helpMenu.addAction(self.aboutAction) - self.aboutAction.triggered.connect(self.aboutDialog.open) - - self.newFileAction = QAction("New File", self) - self.fileMenu.addAction(self.newFileAction) - self.newFileAction.triggered.connect(self.newFile) - self.saveFileAction = QAction("Save File", self) - self.fileMenu.addAction(self.saveFileAction) - self.saveFileAction.triggered.connect(self.saveFile) - self.closeFileAction = QAction("Close File", self) - self.fileMenu.addAction(self.closeFileAction) - self.closeFileAction.triggered.connect(self.closeFile) - - cascadeAction = QAction("Cascade windows", self) - cascadeAction.triggered.connect(self.mdiArea.cascadeSubWindows) - self.viewMenu.addAction(cascadeAction) - tileAction = QAction('Tile windows', self) - tileAction.triggered.connect(self.mdiArea.tileSubWindows) - self.viewMenu.addAction(tileAction) - - # Load settings: - self.settings = QSettings('windelsoft', 'lcfoside') - self.loadSettings() - - def addComponent(self, name, widget): - dw = QDockWidget(name) - dw.setWidget(widget) - dw.setObjectName(name) - self.addDockWidget(Qt.RightDockWidgetArea, dw) - self.viewMenu.addAction(dw.toggleViewAction()) - - # File handling: - def newFile(self): - ce = CodeEdit() - w = self.mdiArea.addSubWindow(ce) - ce.show() - - def saveFile(self): - ac = self.activeMdiChild() - if ac: - ac.saveFile() - - def saveAll(self): - pass - - def openFile(self): - # TODO - pass - - def closeFile(self): - ac = self.activeMdiChild() - if ac: - self.mdiArea.removeSubWindow(ac) - - def loadFile(self, filename): - # Find existing mdi widget: - wid = self.findMdiChild(filename) - if wid: - self.mdiArea.setActiveSubWindow(wid.parent()) - return wid - - # Create a new one: - ce = CodeEdit() - source = self.project.loadProjectFile(filename) - ce.setSource(source) - self.mdiArea.addSubWindow(ce) - ce.show() - return ce - - # MDI: - def activeMdiChild(self): - aw = self.mdiArea.activeSubWindow() - if aw: - return aw.widget() - else: - return None - - def findMdiChild(self, filename): - for window in self.mdiArea.subWindowList(): - wid = window.widget() - if wid.filename == filename: - return wid - return None - - def allChildren(self): - c = [] - for window in self.mdiArea.subWindowList(): - wid = window.widget() - c.append(wid) - return c - - # Settings: - def loadSettings(self): - if self.settings.contains('mainwindowstate'): - self.restoreState(self.settings.value('mainwindowstate')) - if self.settings.contains('mainwindowgeometry'): - self.restoreGeometry(self.settings.value('mainwindowgeometry')) - if self.settings.contains('openedproject'): - projectfile = self.settings.value('openedproject') - self.loadProject(projectfile) - - def closeEvent(self, ev): - self.settings.setValue('mainwindowstate', self.saveState()) - self.settings.setValue('mainwindowgeometry', self.saveGeometry()) - if self.project: - self.settings.setValue('openedproject', self.project.filename) - # TODO: ask for save of opened files - ev.accept() - - # Error handling: - def nodeSelected(self, node): - ce = self.activeMdiChild() - if not ce: - return - if node.location: - row, col = node.location - ce.highlightErrorLocation( row, col ) - else: - ce.clearErrors() - - def errorSelected(self, err): - row, col, msg = err - ce = self.activeMdiChild() - if not ce: - return - ce.highlightErrorLocation(row, col) - - # Project loading: - def loadProject(self, filename): - self.project = Project(filename) - self.projectview.setProject(self.project) - - def openProject(self): - filename = QFileDialog.getOpenFileName(self, \ - "Choose project file", "", "lcfos Project files (*.lcp)") - if filename: - self.loadProject(filename) - - # Build recepy: - def buildFile(self): - """ Build project """ - self.saveAll() - self.buildOutput.clear() - self.buildOutput.append(str(self.compiler)) - mods = self.compiler.compileProject(self.project) - - self.builderrors.setErrorList(self.compiler.errorlist) - self.astViewer.setAst(mods[0]) - for err in self.compiler.errorlist: - self.buildOutput.append(str(err)) - self.buildOutput.append("Done!") - diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/lcfos.png Binary file python/apps/lcfos.png has changed diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/runbuild.py --- a/python/apps/runbuild.py Sun Oct 07 17:04:10 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -import sys - -# Compiler imports: -from compiler.compiler import Compiler -from project import Project - -if __name__ == '__main__': - if len(sys.argv) < 2: - print('Use {0} projectfile'.format(sys.argv[0])) - sys.exit(-1) - filename = sys.argv[1] - project = Project() - project.load(filename) - pc = Compiler() - pc.compileProject(project) - diff -r 32078200cdd6 -r 4a27c28c7d0f python/apps/runide.py --- a/python/apps/runide.py Sun Oct 07 17:04:10 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -import sys -from PyQt4.QtGui import QApplication - -# Compiler imports: -from compiler.compiler import Compiler -from ide.ide import Ide - -if __name__ == '__main__': - app = QApplication(sys.argv) - ide = Ide() - ide.compiler = Compiler() - ide.show() - app.exec_() - diff -r 32078200cdd6 -r 4a27c28c7d0f python/libs/widgets/astviewer.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/libs/widgets/astviewer.py Sun Oct 07 17:13:47 2012 +0200 @@ -0,0 +1,36 @@ +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +def astToNamedElement(astNode, parentNode): + """ Helper to convert and AST tree to NamedElement tree: """ + item = QStandardItem(str(astNode)) + item.setData(astNode) + parentNode.appendRow(item) + for c in astNode.getChildren(): + astToNamedElement(c, item) + +# The actual widget: +class AstViewer(QTreeView): + sigNodeSelected = pyqtSignal(object) + def __init__(self, parent=None): + super(AstViewer, self).__init__(parent) + self.setHeaderHidden(True) + self.clicked.connect(self.selectHandler) + + def setAst(self, ast): + """ Create a new model and add all ast elements to it """ + model = QStandardItemModel() + if ast: + astToNamedElement(ast, model.invisibleRootItem()) + self.setModel( model ) + self.expandAll() + + def selectHandler(self, index): + if not index.isValid(): + return + model = self.model() + item = model.itemFromIndex(index) + node = item.data() + self.sigNodeSelected.emit(node) + + diff -r 32078200cdd6 -r 4a27c28c7d0f python/libs/widgets/codeeditor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/libs/widgets/codeeditor.py Sun Oct 07 17:13:47 2012 +0200 @@ -0,0 +1,140 @@ +from PyQt4.QtCore import * +from PyQt4.QtGui import * +import compiler.lexer +import os.path + +class MySyntaxHighlighter(QSyntaxHighlighter): + def __init__(self, parent=None): + super(MySyntaxHighlighter, self).__init__(parent) + # Syntax highlighting: + self.rules = [] + fmt = QTextCharFormat() + fmt.setForeground(Qt.darkBlue) + fmt.setFontWeight(QFont.Bold) + for kw in compiler.lexer.keywords: + pattern = '\\b'+kw+'\\b' + self.rules.append( (pattern, fmt) ) + + # Comments: + fmt = QTextCharFormat() + fmt.setForeground(Qt.gray) + fmt.setFontItalic(True) + pattern = '\{.*\}' + self.rules.append( (pattern, fmt) ) + + # Procedure: + fmt = QTextCharFormat() + fmt.setForeground(Qt.blue) + fmt.setFontItalic(True) + #pattern = '(?<=procedure )[A-Za-z]' + # TODO lookbehind does not work, think something else + #self.rules.append( (pattern, fmt) ) + + def highlightBlock(self, text): + for pattern, fmt in self.rules: + expression = QRegExp(pattern) + index = expression.indexIn(text) + while index >= 0: + length = expression.matchedLength() + self.setFormat(index, length, fmt) + index = expression.indexIn(text, index + length) + +class LineNumberArea(QWidget): + def __init__(self, codeedit): + super(LineNumberArea, self).__init__(codeedit) + self.codeedit = codeedit + # TODO: display error in this: self.setToolTip('hello world') + def sizeHint(self): + return QSize(self.codeedit.lineNumberAreaWidth(), 0) + def paintEvent(self, ev): + self.codeedit.lineNumberAreaPaintEvent(ev) + +class CodeEdit(QPlainTextEdit): + def __init__(self, parent=None): + super(CodeEdit, self).__init__(parent) + # members: + self.isUntitled = True + self.filename = None + self.setFont(QFont('Courier')) + self.lineNumberArea = LineNumberArea(self) + + self.blockCountChanged.connect(self.updateLineNumberAreaWidth) + self.updateRequest.connect(self.updateLineNumberArea) + + # Syntax highlighter: + self.highlighter = MySyntaxHighlighter(self.document()) + + def setFileName(self, filename): + self.filename = filename + self.isUntitled = False + self.setWindowTitle(filename) + def setSource(self, source): + self.setPlainText(source) + + def save(self): + pass + def saveAs(self): + pass + + def saveFile(self): + if self.isUntitled: + self.saveAs() + else: + source = str(self.toPlainText()) + f = open(self.filename, 'w') + f.write(source) + f.close() + + def highlightErrorLocation(self, row, col): + tc = QTextCursor(self.document()) + tc.clearSelection() + tc.movePosition(tc.Down, tc.MoveAnchor, row - 1) + tc.movePosition(tc.Right, tc.MoveAnchor, col - 1) + tc.movePosition(tc.NextCharacter, tc.KeepAnchor) # Select 1 character + selection = QTextEdit.ExtraSelection() + lineColor = QColor(Qt.red).lighter(160) + selection.format.setBackground(lineColor) + #selection.format.setProperty(QTextFormat.FullWidthSelection, True) + selection.cursor = tc + self.setExtraSelections( [ selection ] ) + def clearErrors(self): + self.setExtraSelections( [ ] ) + + def lineNumberAreaWidth(self): + digits = 1 + mx = max(1, self.blockCount()) + while mx >= 10: + mx = mx / 10 + digits += 1 + space = 3 + self.fontMetrics().width('8') * digits + return space + def lineNumberAreaPaintEvent(self, ev): + painter = QPainter(self.lineNumberArea) + painter.fillRect(ev.rect(), Qt.lightGray) + block = self.firstVisibleBlock() + blockNumber = block.blockNumber() + top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top() + bottom = top + self.blockBoundingRect(block).height() + while block.isValid() and top <= ev.rect().bottom(): + if block.isVisible() and bottom >= ev.rect().top(): + num = str(blockNumber + 1) + painter.setPen(Qt.black) + painter.drawText(0, top, self.lineNumberArea.width(), self.fontMetrics().height(), Qt.AlignRight, num) + block = block.next() + top = bottom + bottom = top + self.blockBoundingRect(block).height() + blockNumber += 1 + def resizeEvent(self, ev): + super(CodeEdit, self).resizeEvent(ev) + cr = self.contentsRect() + self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height() )) + def updateLineNumberAreaWidth(self, newBlockCount): + self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) + def updateLineNumberArea(self, rect, dy): + if dy > 0: + self.lineNumberArea.scroll(0, dy) + else: + self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) + if rect.contains(self.viewport().rect()): + self.updateLineNumberAreaWidth(0) +