Blog

Blog

PHODAL

自己动手写编辑器——Lumia Inspired by Atom

继上一篇 node webkit 用javascript打造web native之后,终于算是可以做出一个atom编辑器,只是这里是为了记念一下Nokia的Lumia手机,最后代码见https://github.com/gmszone/lumia

开始之前

需要配置好开发环境,也就是这个,下面是Mac OS下的

https://github.com/rogerwang/node-webkit

找到对应的版本

将app复制到Application,接着把下面的alias添加到~/.bash_profile

# alias to nw 
alias nw="/Applications/node-webkit.app/Contents/MacOS/node-webkit"

应用一下

source ~/.bash_profile

启动应用的方式是

nw app.nw

启动之前需要

 zip -r app.nw *

这样我们就有了一个基本的框架了

lumia 0.1

最初的版本是来自

https://github.com/zcbenz/nw-sample-apps

也就是node-webkit官网给的示例

只是这示例看上去一点都不友好,在写这篇文章时Lumia编辑器的截图

看上去和atom editor像极了,因为css就是参考atom编辑器的。

框架组成

目前主要是由下面两个构成基本的功能

  • Node.js
  • Node-webkit
  • CodeMirror

显然Node-Webkit发挥了主要的功能,但是CodeMirror也功不可没,作为一个在线编辑器的工具,其他目前的工作者。

  • Underscore JS扩展库
  • StringJS 让对String的处理更加简单

不过Package.json里面有更多的库,但是还没有发挥作用。

Editor 基本功能

暂时没有对Node-Webkit的示例做了太多的修改,修改主要基于下面几个部分

  • 去除了Save、Open、New三个button,直接改为了快捷键
  • 美化UI
  • 扩充了匹配,支持更多的语言
  • Sublime快捷键绑定

所以,他的基本功能就是

  • 打开、保存、新建文件
  • 和Sublime一样的快捷键
  • Atom一样的外观
  • 搜索

Javascript+HTML+Node-Webkit

HTML+CSS主要是对界面的美化

        <body style="background:#fff">
            <div class="workspace" background="#fff">
                <div class="horizontal">
                    <div class="vertical">
                        <div class="panes">
                            <div class="pane" style="min-width:100%;">
                                <ul tabindex="-1" class="list-inline tab-bar inset-panel">
                                    <li class="tab active">
                                        <div class="title" id="title">untitled</div>
                                        <div class="close-icon"></div>
                                    </li>
                                </ul>
                                <div class="editor">
                                <div id="editor"></div>
                                </div>
                            </div>
                        </div>

                        <div class="status-bar tool-panel panel-bottom">
                            <div class="flexbox-repaint-hack">
                                <div class="status-bar-left">
                                    <div class="cursor-position inline-block" id="mode">
                                        </a>
                                        <div class="status-image inline-block">
                                            <span class="image-size" style="display: none;"></span>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <input style="display:none;" id="newFile" type="file" />
                    <input style="display:none;" id="openFile" type="file" />
                    <input style="display:none;" id="saveFile" type="file" nwsaveas />
                </div>

                <div style="display:none">
                    <button style="display:none;" id="new">New</button>
                    <button style="display:none;" id="open">Open</button>
                    <button style="display:none;" id="save">Save</button>
                </div>
        </body>

如把title移到了最上面,变成了和title一样的位置,看上去像极了chrome浏览器的标签,这里主要是用了CSS的伪元素

        .tab-bar .tab:before {
            content:'';
            position: absolute;
            top: -1px;
            left: -16px;
            height: 26px;
            width: 30px;
            border-top-left-radius: 3px;
            border-left: 1px solid #b9b9b9;
            box-shadow: inset 1px -1px 1px rgba(0, 0, 0, 0.05);
            -webkit-transform: skewX(133deg);
        }
        .tab-bar:after {
            content:"";
            position: absolute;
            bottom: 0;
            height: 5px;
            left: 0px;
            width: 100%;
            background-color: #f0f0f0;
            border-bottom: 1px solid #dddddd;
            border-top: 1px solid #b9b9b9;
        }

这些主要就是表现在外见上的,大致上就是上面的作法。

editor.js里面做的主要就是判断和处理事件了

           onload = function() {

            newButton = document.getElementById("new");
            openButton = document.getElementById("open");
            saveButton = document.getElementById("save");

            newButton.addEventListener("click", handleNewButton);
            openButton.addEventListener("click", handleOpenButton);
            saveButton.addEventListener("click", handleSaveButton);

            $("#saveFile").change(function(evt) {
                onChosenFileToSave($(this).val());
            });
            $("#openFile").change(function(evt) {
                onChosenFileToOpen($(this).val());
            });    
            editor = CodeMirror(
                document.getElementById("editor"), {
                    lineNumbers: true,
                    keyMap: "sublime",
                    autoCloseBrackets: true,
                    matchBrackets: true,
                    showCursorWhenSelecting: true,
                    extraKeys: {
                        "Cmd-N": function(instance) {
                            handleNewButton()
                        },
                        "Ctrl-N": function(instance) {
                            handleNewButton()
                        },
                        "Cmd-O": function(instance) {
                            handleOpenButton()
                        },
                        "Ctrl-O": function(instance) {
                            handleOpenButton()
                        },
                        "Cmd-S": function(instance) {
                            handleSaveButton()
                        },
                        "Ctrl-S": function(instance) {
                            handleSaveButton()
                        },
                        "Cmd-Shift-P": function(instance) {
                           console.log("hello".green)
                        }
                    }
                });
            newFile();
            onresize();

            gui.Window.get().show();
        };

可以看到的是editor主要是由CodeMirror驱动的,还有就是对于语言的判断,这个是用于判断是哪种语言并给予高亮。

        function handleDocumentChange(title) {
            var mode = "javascript";
            var modeName = "JavaScript";
            if (title) {
                title = title.match(/[^/]+$/)[0];
                document.getElementById("title").innerHTML = title;
                document.title ="Lumia"+title;
                _.each(m.allmodes, function(modes) {
                    if (S(title).contains(modes["filename"])) {
                        mode = modes["mode"];
                        modeName = modes["modeName"];
                        console.log(mode);
                    }
                });
            } else {
                document.getElementById("title").innerHTML = "[no document loaded]";
            }
            editor.setOption("mode", mode);
            document.getElementById("mode").innerHTML = modeName;
        }

主要的工作是underscorestringjs做的,这里的m用的是外部文件。

     var m = require('./allmodes')

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签