marijnhaverbeke.nl/blog

フィード

記事のアイキャッチ画像
CodeMirror 6.0
marijnhaverbeke.nl/blog
CodeMirror 6 is a new code editor libraryfor the web, a from-scratch implementation based on the experience ofbuilding and maintaining versions 1 to 5 for the past 13 years. Itaims to be more extensible and accessible than previous versions.As of today, version 6.0 is stable. Going forward, probably at leastseveral years, all new releases will be under the 6 major version, andbe backwards compatible.The library has been usable and largely stable for over a year, withonly minor breaking changes. I generally prefer to release late, toavoid having too many regrettable mistakes slip into the stablerelease, which would then have to be kept there indefinitely. Withouta doubt there will be things that, a year from now, I wish I hadreleased differently, but by having users work with the code inproduction for a significant amount of time, a lot of small issues andsources of friction have been found and resolved before being set downin stone.Work on this system started four years ago, with Proto
2年前
記事のアイキャッチ画像
Facets as Composable Extension Points
marijnhaverbeke.nl/blog
An extensible system, at its base, is a system that allows people toadd additional functionality that was not anticipated by the coresystem.A good extensible system also makes sure multiple extensions thatdon't know anything about each other can be combined, and compose inways that don't cause problems.The problem has several aspects.Composition: Multiple extensions attaching to a given extensionpoint must have their effects combined in a predictable way.Precedence: In cases where combining effects isorder-sensitive, it must be easy to reason about and control theorder of the extensions.Grouping: Many extensions will need to attach to a number ofextension points, or even pull in other extensions that they dependon.Change: The effect produced by extensions may depend on otheraspects of the system state, or be explicitly reconfigured.This post tries to explain CodeMirror's(a code editor library) approach to solving this problem.Facets and the Editor StateA facet, in this system, defines
2年前
記事のアイキャッチ画像
CodeMirror 6 Enters Beta
marijnhaverbeke.nl/blog
I'm happy to announce that with version 0.8.0 the CodeMirror6 project is entering its beta phase,and you're very much invited to start trying it out and poking at it.Roughly, this “beta” status means:I actually like the current programming interface. Apart from themove from a mono-package to a group of separate packages (removingthe next/ in the package names), no significant breaking changesare planned. There might still be breaking changes in peripheralparts of the interface, when real-world experience shows thecurrent interface to be problematic, but these should be of thetype where a quick scan of the release notes and some greppingthrough your code are enough to adapt to them.The system is no longer a hopeless unfinished mess. In fact, itseems to work pretty well. Which isn't to say it won't break inyour use case, of course, since it has seen little practical useyet and you're likely to be the first to do some specific thingwith it.I've put together enoughdocumentation for people
4年前
記事のアイキャッチ画像
Collaborative Editing in CodeMirror
marijnhaverbeke.nl/blog
This post describes the considerations that came up in designing thedocument-change data structure and built-in collaborative editingfeature in the upcoming version ofCodeMirror (a code editor system). It is something of a followup tothe Collaborative Editing inProseMirror post.I won't introduce anything new or exciting here—the design I ended upwith is a very boring non-distributed operationaltransformation.In a way, this post is publishing a negative result: I looked into abunch of interesting alternative approaches, but found they didn'tmeet the requirements for this system.Since collaborative editing is a tricky field with a lot of differentsolutions, all of which have their awkward trade-offs, I think thepath towards this boring end result might still provide a usefulresource for people working on similar systems.Distributed versus coordinated collaborative editingThere's quite a disconnect between the scientific literature oncollaborative editing and what most collaborative edito
4年前
記事のアイキャッチ画像
CodeMirror MOSS project report
marijnhaverbeke.nl/blog
Development of CodeMirror 6 this year has been generouslysupported by Mozilla throughtheir MOSS program.The MOSS program asks for a closing retrospective blog post describingthe progress made during the grant period. I've written about theprogress in the first 9 months of the year in my status updatepost from August. To summarize, in thatperiod we:Implemented solid text composition support.Designed a powerful extension system.Documented the system with doc comments and set up a process togenerate HTML documentation from that.Created a parser system andintegrated it with syntax highlighting.Implemented and improved various less glamorous subsystems(styling, gutters, in-text widgets).The past few months have been focused more on concrete user-visiblefeatures, instead of big architectural questions. We've added thesefeatures:Code folding support.A generic extension for showing UI panels above and below theeditor.A search and replace interface.A generic tooltip extension.Support for in-edi
4年前
記事のアイキャッチ画像
Lezer
marijnhaverbeke.nl/blog
I keep coming across people who consider parser technology aforbidding, scary field of programming. This is nonsense—a smallparser can be very, verysimple,and provide a wholesome exercise in recursive thinking. At the sametime, it is true that you can make parsing extremely complicated.Mostly, this tends to happen when you generalize parsing techniques towork for different grammars. Since compiler textbooks usually describegeneral approaches to parsing, they may be to blame for putting peopleoff.This post describes a new parsingsystem I wrote forCodeMirror, a source code editor. It framesthe system with some history, and digresses into some neatarchitectural details.Editor features like syntax highlighting, bracket matching, codefolding, and autocompletion all involve some level of parsing.Unfortunately, since editors have to handle many different languages,they require a generalized approach to parsing.CodeMirror is in the process of being rewritten, and I wanted toimprove the way it
5年前
記事のアイキャッチ画像
CodeMirror 6 Status Update
marijnhaverbeke.nl/blog
It has been almost a year since we officially announced the CodeMirror6 project, which aims to rewrite CodeMirror(a code editor for in the browser) to align its design with thetechnological realities and fashions of the late 2010s.This post is for you if, in the course of that year, you'veoccasionally wondered what the hell Marijn is doing and if he'sgetting anywhere at all. I have been absolutely terrible aboutcommunicating progress, and even monitoring therepository wouldoften leave you in the dark, as I was working on local branches orentirely different repositories. In any case, the code that's in thereis almost entirely undocumented.Where We AreLast week, I landed a major set of changes, which had been in theworks for about four months. They integrate a new approach to codeparsing. This was the last piece of the system that was completely influx, where I didn't want to nail down anything related to it yetbecause I wasn't sure how it would end up working.Now I am sure, which means
5年前
記事のアイキャッチ画像
Computing Indentation from Syntax Trees
marijnhaverbeke.nl/blog
Most code editors have some concept of automatic indentation. For eachlanguage, they have some logic that can compute an appropriateindentation for a given line. They may immediately indent when you pressEnter, or require some interaction to reindent a line or selection.Even with the cleverest tools, there isn't always a single definiteindentation for a given line. In a language like Python, whether aline should continue the current block or dedent to some parent blockisn't obvious from the context. Inside multi-line strings or comments,an editor can only guess what the user is trying to do. But eventhere, having a guessed indentation that you may need to correct isusually more convenient than having to manually indent every line.In the process of maintaining CodeMirror,I've written a lot of indentation logic for a lot of languages.Initially directly on top of the source string, by applying heuristicregexps to the ends of previous lines (which was as fragile as itsounds). Then from dat
5年前
記事のアイキャッチ画像
Extensible Extension Mechanisms
marijnhaverbeke.nl/blog
(The details at the bottom of this post no longer reflect the waythings work in CodeMirror 6. See this post for anupdated description.)It has become fashionable to structure big systems as a number ofseparate packages. The driving idea is that it is better to, insteadof locking people into your implementation of a feature, provide thefeature as a separate package that they can load alongside the coresystem.This gets you, roughly...The ability to not even load features you don't need, which isespecially helpful in client-side systems.The possiblity of replacing functionality that doesn't serve yourpurpose with another implementation. This also reduces the pressureon the core modules to cover every possible use case.A reality check for the core interfaces—by implementing basicfeatures on top of the client-facing interface, you are forced tomake that interface at least powerful enough to support thosefeatures, making sure that things just like them can be built with3rd-party code.Isolatio
5年前
記事のアイキャッチ画像
ProseMirror 1.0
marijnhaverbeke.nl/blog
Two years ago, I started the ProseMirrorproject because I wanted to take a stab at abetter approach to WYSIWYG-style editing. Today,I'm releasing version1.0 of thelibrary. The architecture and scopeof the project have changed quite a bit during its lifetime, but Ifeel that the original goal has been met.ProseMirror is a Web interface component, and though some of thechallenges it tackles are specific to the strengths and (especially)weaknesses of the Web platform, don't think of it as another TinyMCEalternative. Rather, it is a more general take on rich text editingthat happens to be implemented in JavaScript for the browser.Schema-based editingMost importantly, ProseMirror is agnostic to the actual documentshape, making it possible to build applications on top of this librarythat in the past would have required a fully custom editorimplementation.What I mean by being agnostic to document shape is ProseMirror'sschema feature. The coreeditor has no built-in opinion about what a document
7年前
記事のアイキャッチ画像
My JavaScript Build Setup
marijnhaverbeke.nl/blog
I am looking forward to the time where mynode.js and browser can natively run ECMAScript6 code. Build steps are a pain, and one of the reasons I likeJavaScript in the first place is the (traditional) absence of “waitingfor the compiler” from my work-flow.You can question whether ECMAScript 6 is worth incurring theinconvenience of a build step. I think it is. The various syntacticniceties provided by that dialect, mostly arrow functions and classsyntax, make my code feel cleaner.And even without ECMAScript 6, to run modularly-written things in abrowser we'd still traditionally require a bundling step.Let's do away with that first.Unbundled code in the browserBundling tends to be the thing at the very end of the pipeline. Youhave a “root” script, and your bundler follows all its dependencies,dumps them into a single big file, with some magic glue that makes theright module pop up in the right place. You then refer to this bundlefrom a <script> tag.One thing about bundling is that it is m
8年前
記事のアイキャッチ画像
Being a Resident at the Recurse Center
marijnhaverbeke.nl/blog
I spent one week being a resident at the Recurse Center in New Yorkthis November. The Recurse Center is a somewhat unusual institution.They call themselves a “programmers retreat”. That more or less meansthey are a space where people who want to improve their programmingskills (whether beginners or experienced programmers) hang around fora few months, doing self-directed learning, collaborating with eachother, and getting inspired by the work, talks, and discussion of thepeople around them.If that still doesn't make a lot of sense, see theirwebsite or read Martin Kleppmann'sexcellent description.Being a resident there meant that I was simply present in the spacefor a week, gave a talk, and worked with people on their projects.Working with experienced programmers is a way to diffuse some of theknowledge of those programmers, as well as a good way to make peoplerealize that those who are considered good programmers still have toconstantly google for stuff and make stupid mistakes.PeopleT
8年前
記事のアイキャッチ画像
Collaborative Editing in ProseMirror
marijnhaverbeke.nl/blog
This post describes the algorithm used to make collaborative editingwork in ProseMirror. For an introduction to ProseMirror, seeanother post here.The ProblemA real-time collaborative editing system is one where multiple peoplemay work on a document at the same time. The system ensures that thedocuments stay synchronized—changes made by individual users are sentto other users, and show up in their representation of the document.Since transmitting changes over any kind of network is going to taketime, the complexity of such systems lies in the way they handleconcurrent updates. One solution is to allow users to lock thedocument (or parts of it) and thus prevent concurrent changes fromhappening at all. But this forces users to think about locks, and towait when the lock they need is not available. We'd prefer not to dothat.If we allow concurrent updates, we get situations where user A anduser B both did something, unaware of the other user's actions, andnow those things they did have to b
9年前
記事のアイキャッチ画像
ProseMirror
marijnhaverbeke.nl/blog
Sometimes I lie awake at night, feverishly searching for new ways toload myself down with more poorly-paying responsibilities. And then itcomes to me: I should start another open-source project!Well, that's not really what happens. But the effect is the same. Ikeep building complex, demanding pieces of code and then giving themaway. The actual mechanism is usually that I think of some technicalconcept, find out that it hasn't been done yet, and through some mixof curiosity and ego, just have to see if I can do it.Here's the newest damage: ProseMirror, a browser-based rich texteditor. Though I'm not giving it away per se, but running acrowd-funder to open-source it, and havethought a bit about how to makeafter-release maintenance sustainable.An Editor?Didn't I just talk about implementing things that have “not been doneyet”? And aren't there at least a hundred browser-based rich texteditors out there?Yes, and yes. But none of the existing projects take the approach thatI think would be
9年前
記事のアイキャッチ画像
More Money For Better Open-Source Software
marijnhaverbeke.nl/blog
As a programmer I create artifacts that, unlike classical commoditiessuch as loaves of bread or toothbrushes, can be copied at zero cost.Fitting copyable things into the capitalist market economy issomething of an unsolved problem. The standard model of commerce is tosell our products by the unit. But here, everyone who gets their handson a single unit can create an infinite number of units, ruining ourmarket.Thus, we've invented copyright, using the law to try and make thecopyable less copyable, and allowing us to go back to the classicalmodel of selling by the unit. Copyright is rather effective atprotecting the interests of the producer of the copyable goods—itdoesn't fully prevent copying, but it inhibits it enough to allow manyauthors, musicians, and software houses to turn a profit.Open source software (and similarly open-licensed works in othermedia) tilts the balance the other way—it leaves the consumer'sability to copy the works largely unconstrained. This is a way tooptimize
9年前
記事のアイキャッチ画像
Display Updates in CodeMirror
marijnhaverbeke.nl/blog
This post is part of the series about the internalsof CodeMirror. This time, we'll discuss theway CodeMirror schedules updates to the DOM.The problem is this: CodeMirror has an internal model of the documentthat is being edited, and is displaying a representation of this modelon the screen, using the browser's DOM. These have to be keptsynchronized, for obvious reasons. However, updating the DOM iscostly, especially if you are going to interleave those updates withreading layout information from it.Most changes to CodeMirror's display require re-positioning the cursoror selection. To do this, CodeMirror reads layout information tofigure out the exact placement of the relevant characters. Thus, ifevery function that touches CodeMirror's model of the document wentahead an immediately updated the DOM display, the result would bemind-bogglingly slow.This is somewhat similar to the problem that is being solved by theReact library. That libraryimplements a “shadow DOM” data structure, which
10年前
記事のアイキャッチ画像
Eloquent JavaScript's Build System
marijnhaverbeke.nl/blog
A text is not just a string of Unicode. It has structure, and acertain internal coherence. People might want to read it in differentformats, and if I am going to hand-edit it, I want its source to be inan editing-friendly format.For the first edition ofmy book (Eloquent JavaScript), I wrote my own markup format style anda Haskell program to parse it. That was, of course, a bigwaste of time and made it needlessly hard for people to contribute tothe book or translate it.When writing the second edition, Iused AsciiDoc as a sourceformat, since I had had good experiences with that before. This workedreasonably well, though I'll discuss some problems I had with itlater.The nice thing about being both a programmer and an author is that youcan build custom tooling for the text you are working on. Whilerewriting Eloquent JavaScript, I built up a big suite of tools andscripts to build, check, and customize the book. This blog postsdescribes those tools. They are probably of no direct use to anyo
10年前
記事のアイキャッチ画像
Review: If Hemingway Wrote JavaScript
marijnhaverbeke.nl/blog
Four out of five stars. Would read again. ★★★★☆When Angus Croll first told me that he was working on a book thatsolves simple programming exercises in the style of various famousliterary authors, I couldn't help but suspect that such a book wouldbe completely pointless, and he wouldn't sell ten copies. Afterreading the result of his efforts, I still firmly stand by the latterprediction, but I have to admit that I actually enjoyed this book.If Hemingway Wrote JavaScript is a slim, prettily typeset book thatis divided into five programming exercises, and has five authors solveeach of the exercises. It accompanies each solution with a briefdescription of the author's background and style, and a detailedwalk-through of the code.Wild experiments are what moves a genre forward. And this is a formatof programming book that's certainly never been tried before.The exercises are very simple—think Fibonacci and factorials. Butsome of the solutions are absolutely charming. Here's Jorge LuisBorges
10年前
記事のアイキャッチ画像
On null and undefined in JavaScript
marijnhaverbeke.nl/blog
This is a brief post to explain why my JavaScript code is full of == null comparison expressions, even though linter software tends todisapprove.JavaScript defines both undefined and null as separate values. Thefirst comes up in many constructs in the core language—anuninitialized variable, a parameter that wasn't passed, a missingfield in an object, or a call to a function that doesn't returnanything all have the value undefined. The null value has the ringof a C null-pointer, and tends to be common in library interfaces—achild-less DOM node has a firstChild property of null,getElementById with a non-existent ID gives you null, and so on.I have found the distinction between null and undefined to bemostly useless—a cognitive burden without merit.The interesting distinction is usually between 'non-values' and actualvalues.The == null (or != null, as it may fit) pattern is...Shorter than == undefined.Much shorter than typeof X == "undefined".True for both undefined and null values, savin
11年前
記事のアイキャッチ画像
Tern
marijnhaverbeke.nl/blog
I spend a rather large fraction of my days inside Emacs, writing andediting JavaScript code. Of this time, a significant amount is spentdoing things that follow patterns. Pattern which, with a littlemachine intelligence, could easily be automated.Years ago, before accidentally rolling into this JavaScript career, Imostly programmed Common Lisp. Emacs integration for CommonLisp is divine. It does just about everything, from interactivelyexploring and modifying a running system, to debugging, to looking updocumentation for you on the web. But the main two things that madeday-to-day programming in that environment so wonderful were that itautomatically showed me a list of argument names when I was typingarguments to a function call, and that it allowed me to jump to thedefinition of something at a single keystroke (and then jump back towhere I came from at another keystroke). This meant that I hardly everhad to break my flow to go look up the interface for a function, orwonder how or wher
11年前
記事のアイキャッチ画像
Shared documents in CodeMirror
marijnhaverbeke.nl/blog
From the very start, CodeMirror was set up as a system with zerounused abstractions.This is a doctrine that I've come to esteem highly: write code thatsolves the current problem you have, and not a bunch of other,similar problems that you can imagine someone may have in the future.And such a system will have to grow, almost without exception, asnew use cases come up. But I argue that a non-minimal system, nomatter how much time was spent on a genius architecture up front, willalso have to change to deal with new realities. I haven't yet met anengineer who was able to accurately predict future uses of hersystems. I certainly can't. Such a more abstract system would havemore code, and thus more inertia—it takes more work to pull it into adifferent direction.All code is, in principle, throw-away code. I might not actually throwit away, but I am prepared to, and fully expecting to, change it inradical, major ways after I write it. Thus, rather than writing codein a way that makes it flexib
11年前
記事のアイキャッチ画像
Parsing line noise as JavaScript
marijnhaverbeke.nl/blog
I am writing a tool that tries to enhance a JavaScript code editor bydoing some halfway serious static analysis on code, as it is beingwritten. To analyze code, you'll want to parse it, because—believeme—running casual regexps over programs in order to determine theirstructure is not a healthy direction to go in. But unfortunately,code that's in the process of being written will, most of the time,not be in a syntactically valid form.Thus, the kind of software that usually passes for a parser is notmuch help. A typical parser understands it as its responsibility todiligently check its input for validity, and complain, rather thanreturn a useful result, when a problem is found.That's not what we want. We need access to an abstract syntax tree,not an error message.One solution that comes to mind is a technique often used by somecompilers: when they come across a syntax error, they'll report it,skip forward to a heuristically determined 'safe point' (often thestart of the next statement, f
11年前
記事のアイキャッチ画像
Postmodern 1.19 released
marijnhaverbeke.nl/blog
Postmodern is a Common Lisp library for communicating with aPostgreSQL database. Sabra Crolleton recently published a nicecollection of examples for the library. That reminded me thatit's been over a year since the last release.There have been several major improvements, including support fornotifications and bulk copying, so a release was inorder, if only to prevent the impression that the library was notbeing maintained.So there it is, Postmodern version 1.19. Get it from theproject page.
11年前
記事のアイキャッチ画像
A tale of a pathological scrolling model
marijnhaverbeke.nl/blog
When you've lied about something, it tends to take more and more liesto cover up the discrepancies created by the first lie. A very similarthing happened when CodeMirror needed to fake its scrollbars.CodeMirror's viewportFirst, some background. Why would we want to fake a scrollbar to beginwith?In order to remain responsive when huge documents are loaded in,CodeMirror does not render the whole document, but only the part of itthat is currently scrolled into view. This means that the amount ofDOM nodes it creates is limited by the size of the viewport, and thebrowser relayouts triggered by changes to the text are relativelycheap.So if you have a document that is higher than the vertical height ofyour editor, the actual DOM inside the editor will be a big, largelyempty div that defines the height (and thus the scrollable area) ofthe editor content, with inside of that, absolutely positioned tocover the part that's currently scrolled into view, a smaller elementthat contains the lines of
12年前
記事のアイキャッチ画像
Overloading plain text: CodeMirror marked ranges
marijnhaverbeke.nl/blog
One common feature request that CodeMirror version 1 was fundamentallyunable to support (due to its reliance on contentEditable), andwhich was thus built into version 2 from the start, isprogrammatically styling stretches of text. In version 2, you can callinstance.markText(from, to, className), and it'll style that stretchof text with the given CSS class.By version 2.16, it was actually possible to edit the text around andinside such a marked range without strange corruptions occurring. Thatversion also added a way to query the marked range for its currentposition (if any) within the document.Then, last month in version 2.34, marked ranges were integrated withthe undo history, so that if you delete a stretch of text thatcontains marked ranges, undoing the deletion will bring back theranges, not just the text.And last week, prompted by use cases from two different customers, Idecided to add a number of rather radical extensions to this API. Inthe current code in the v3 (future version
12年前
記事のアイキャッチ画像
Acorn: yet another JavaScript parser
marijnhaverbeke.nl/blog
Acorn is a JavaScript parser written in JavaScript.Another one.Just like:The original UglifyJS parserThe new UglifyJS parserZeParserThe Narcissus project's parserEsprimaAcorn produces a well-documented, widely used AST format.The same as the last two parsers in that list.Acorn is really fast. Just like the last one in the list: Esprima.Acorn is tiny. About half as big as Esprima, in lines of code.Still, there's no good reason for Acorn to exist. Esprima is anexcellent project, well-documented, and small enough for any practicaluse. It exposes an interface very similar to Acorn.The only reason I wrote Acorn is that small, well-defined systems areso much fun to work with, and that Esprima's web page verytriumphantly declared it was faster than parse_js, theimplementation in UglifyJS version 1, which is a port of my ownparse-js Common Lisp library.I just had to see if I could do better.Turns out I can. Acorn beats Esprima, at least on Chrome, Firefox, andOpera (I didn't test other browser
12年前
記事のアイキャッチ画像
CodeMirror's mode system
marijnhaverbeke.nl/blog
A CodeMirror mode is a module that helps CodeMirror, a codeeditor, highlight and optionally smart-indent text in a specificprogramming language or other kind of structured text format.Code editors take widely different approaches to the way syntaxhighlighting styles are defined. An elegantly simple approach isoutlined in Patrick Walton'sCommon JavaScript Syntax Highlighting Specification,basically defining a state machine with regular expressions as itsedges. Unfortunately, this proposal was never widely adopted.ACE uses a similar, though incompatible system. Other, moreheavyweight, and often downright obscure, systems are found inEmacs, Vim, or Kate.The interruptable, resumable parserCodeMirror takes its own unconventional approach to mode definition.It grew more or less organically out of my fondness for crazy hacks atthe time I was writing the first version of CodeMirror.The original approach modeled a mode as a transforming iterator(iterator in the Python sense)—an iterator that la
12年前
記事のアイキャッチ画像
CodeMirror 2.34 and 3.0beta1 released
marijnhaverbeke.nl/blog
I've just marked the current state of the master branch as version2.34. The main changes are:New mode: Common LispFix right-click select-all on most browsers.Change the way highlighting happens:Saves memory and CPU cycles.compareStates is no longer needed.onHighlightComplete no longer works.Integrate mode (Markdown, XQuery, CSS, sTex) tests in the central testsuite.Add a CodeMirror.version property.More robust handling of nested modes in formatting and closetag plug-ins.Un/redo now preserves marked text and bookmarks.See github for a full list of patches. Get the zip file fromthe website.2.34 will be the last 'full' release on the 2.x branch. I willcontinue to bring out bugfix releases on that branch for at least twomore months, but new work will, from now on, happen on version 3.The first beta version of CodeMirror 3 also came out today. The jumpto version 3 is mostly a result of some of the major work I did lastmonth, that the community generously sponsored. Some ofthat work required
12年前
記事のアイキャッチ画像
Faking an editable control in browser JavaScript
marijnhaverbeke.nl/blog
This is a post in the cm-internals series, describing theinternals of the CodeMirror editor.The problem it tackles is this: you are writing a JavaScript controlthat needs to act as a text input field—it must be focusable, supportcopy and paste, receive typed input—but really isn't. I.e. you want todraw it yourself, and have full control over its content.In this post, I won't talk about drawing a cursor, maintaining yourown selection, and similar. Those are also required to present aconvincing text input, of course. But they are relativelystraightforward to implement.The hidden textareaThe crux of my solution, the initial inspiration for which I got fromthe ACE editor, revolves around a hidden textarea node.This is the thing that the browser believes is focused when theeditor looks like it is focused. It'll behave like a regular focusableobject, you can assign a tabindex to it, and will receive focusand blur events when it gains or loses focus, allowing us to updatethe style of our edit
12年前
記事のアイキャッチ画像
JavaScript closure vs. object look-up performance
marijnhaverbeke.nl/blog
Note: the question asked in this post, "why aren't closuressuper-fast?", was thorougly answered by Vyacheslav Egorov inhis followup. Reading that is probably more informative thanreading the text below.I originally structured CodeMirror instances as one huge closurethat contained all the internal variables. The constructor wouldcreate local variables for all internal state, and local functions foreverything that needed access to that state, and then return an objectthat contained the API methods, which themselves also closed over allthose internals. Something like this:function CodeMirror(args) { // Internal state var doc = something, selection = somethingElse; // Internal functions function modifyDoc(from, to, newText) { /* directly access doc, selection, etc */ } function drawDoc() { /* ... */ } return { getLine: function(n) { return getLineFrom(doc, n); }, refresh: drawDoc /* etc */ };}I had several reasons for doing it like this. Firstly, it minifieswell—local variables are very ea
12年前
記事のアイキャッチ画像
CodeMirror's document representation
marijnhaverbeke.nl/blog
This post is part of an ongoing series of articles that aim todocument the internals of the CodeMirror code editor. I will usethe cm-internals tag to distinguish these posts—if you intend tohack on CodeMirror, it might be worthwhile to see what else is there.The problemThe initial implementation of CodeMirror 2 represented the document asa flat array of line objects. This worked quite well—splicing arrayswill require the part of the array after the splice to be moved, butthis is basically just a simple memmove of a bunch of pointers, soit is cheap even for huge documents.However, in version 2.17 (November 2011), I added support for linewrapping and code folding. Once lines start taking up a non-constantamount of vertical space, looking up a line by vertical position(which is needed when someone clicks the document, and to determinethe visible part of the document during scrolling) can only be donewith a linear scan through the whole array, summing up line heights asyou go. One of the d
12年前
記事のアイキャッチ画像
Heckle, or how I shall try to blog again
marijnhaverbeke.nl/blog
Last week, the need for a platform to publish my bi-directional textstory on forced me to think about blogging software once more.I had heard that all the cool kids are now using Jekyll ontheir Github pages to publish their blogs. I am not keen ondepending on Github for yet another aspect of my online life, but theidea of generating a static site from a git repository does soundappealing.Setting up a simple site with Jekyll was a breeze. It really is awell-designed approach. But I also immediately ran into itslimitations. Something as simple as sorting my list of tags by theamount of posts they contain was... apparently not possible withoutmonkey-patching some classes from a plug-in.Now I have all the respect in the world for the Ruby community andtheir anarchist approach to modularity, but such shenigans just don'tfit my own sense of aesthetic. On the other hand, templating languageslike Liquid, which Jekyll uses, are not nearly anarchist enough for mytaste—they strictly forbid any ki
12年前
記事のアイキャッチ画像
Cursor motion & bi-directional text
marijnhaverbeke.nl/blog
"Unicode is hard" is a commonplace among developers. And I guess itis hard. Witness the amount of systems that get things like string en-and decoding wrong. And that is the easy part—the real fun starts whenyou need to actually display those strings.Fortunately, toolkits and libraries are able to hide the horrors ofcombining characters, directionality, and word breaking most of thetime. Today, most software has moved beyond the ASCII-only worldview,and makes at least an effort to handle these things properly. Youthrow strings at it, it displays them correctly for you.But there are situations where that doesn't suffice. CodeMirror isa code editor implemented in JavaScript. It relies on the browser todisplay its content, and modern browsers are very good at displayingtext. But it also displays a cursor, and controls its movement. To dothat, it needs to be aware of some non-trivial properties of Unicodetext.In this article, I'll outline the solutions I came up with. I was ableto find very
12年前
記事のアイキャッチ画像
A Gentle Introduction to Machine Fundamentals
marijnhaverbeke.nl/blog
Read this post at http://marijnhaverbeke.nl/turtle/.
13年前
記事のアイキャッチ画像
Announcing: CL-TK
marijnhaverbeke.nl/blog
I've just put my Common Lisp Tcl/Tk bindings online. They differ fromthe existing LTK library in that they...Support for both FFI bindings and talking to a wish shell.Have hardly any 'wrapper' functionality — you're directlydriving a Tcl interpreter from Lisp.They've only been used in one medium-sized project so far, but theyare so simple that I'm rather confident they work as intended.Project page at http://marijn.haverbeke.nl/cl-tk/.
15年前
記事のアイキャッチ画像
Parse-js library released
marijnhaverbeke.nl/blog
I've released the JavaScript parser (in Common Lisp) that I hadsitting around as a proper package. It's small, fast, and complete,but not terribly well-documented. Get it from the project page.
15年前
記事のアイキャッチ画像
Postmodern 1.13 released
marijnhaverbeke.nl/blog
There were a handful of small fixes still sitting around, as well asthe deftable addition. It has been four months since 1.12, so: 1.13!Champagne all around.Download.
16年前
記事のアイキャッチ画像
Announcing: ST-JSON
marijnhaverbeke.nl/blog
After dragging my JSON implementation through various projects, andhaving several incompatible versions exist at the same time, I gotpermission to open-source it from the company that originallypaid me to write it, so it has a home now: ST-JSON.There already exists a comparable library called CL-JSON. Ioriginally wrote a new one because the way CL-JSON uses nil toencode all of boolean false, the empty list, and the empty object wascausing headaches, and later I added some other extensions.
16年前
記事のアイキャッチ画像
HTTP Caching
marijnhaverbeke.nl/blog
(This is something I wrote for a now-defunct web publication in 2008. I've inlined the text here.)Giving caches a chanceThough it tends to get treated as one, HTTP is not just a dumb file-transfer protocol. It allows you, to a certain degree, to specify an intention with your requests (GET/POST, with PUT and DELETE thrown in if you really want to), it has a somewhat structured method for parameter passing, supports content negotiation, does authentication. But what I want to talk about here is caching.Until recently, my experiences with caching had been mostly in the form of fighting against it. Any web developer will have experienced browsers (one of them in particular) inappropriately caching pages and scripts — causing users to see old content, or load broken scripts even after we fixed them. Typically, you then added some magical headers that you found through a random web-search and behold, the browser stops caching and all is well.The reason browsers behave like this is, of cours
16年前
記事のアイキャッチ画像
Interning symbols
marijnhaverbeke.nl/blog
Mike Ajemian wrote something about dynamically interningsymbols using the ~:@ format construct to upcase parts of thesymbol's name. This works fine on standard CLs, but if you want towrite something that also works with Allegro's 'modern' mode (wheresymbols are case-sensitive), you don't want to upcase the symbol. Whatyou do there is use the reader against itself — (format nil "~a-~a" :insert name-symbol),the symbol-name of :insert will be whateverthe reader made of it, and thus you'll get a symbol that follows thesame conventions as the surrounding system.(You also don't want to use uppercase strings in your packagedefinitions — I'm looking at you split-sequence — use #:symbolsyntax if you don't want to waste memory on pointless keywordsymbols.)In a similar vein, sometimes you'll want to create throwaway symbolswith a certain name at run-time. (For example, S-SQL requiressymbols for stuff like database index names, which you might want togenerate.) intern leaks memory in this case, si
16年前