diff --git a/docs/core-packages-and-features/autocomplete.md b/docs/core-packages-and-features/autocomplete.md
index 93f87b1..a5e8529 100644
--- a/docs/core-packages-and-features/autocomplete.md
+++ b/docs/core-packages-and-features/autocomplete.md
@@ -20,4 +20,4 @@ The autocompletion interface is implemented in the {autocomplete-plus} package.
* The core {autocomplete-css} package suggests tag names, CSS property names, and contextually relevant values for properties.
* The core {autocomplete-snippets} package suggests snippets whose prefixes match what has already been typed in the current word.
-Community packages — in particular packages that wrap language servers — can also act as “brains” for autocompletion. Pulsar’s package registry can show you [a list of packages](https://web.pulsar-edit.dev/packages?serviceType=provided&service=autocomplete.provider) that can supply data to `autocomplete-plus`.
+Community packages — in particular [packages that wrap language servers](/ide-features/) — can also act as “brains” for autocompletion. Pulsar’s package registry can show you [a list of packages](https://web.pulsar-edit.dev/packages?serviceType=provided&service=autocomplete.provider) that can supply data to `autocomplete-plus`.
diff --git a/docs/core-packages-and-features/core-packages-and-features.11tydata.json b/docs/core-packages-and-features/core-packages-and-features.11tydata.json
index 788e1c6..fddb610 100644
--- a/docs/core-packages-and-features/core-packages-and-features.11tydata.json
+++ b/docs/core-packages-and-features/core-packages-and-features.11tydata.json
@@ -18,7 +18,7 @@
},
{
"text": "Version control",
- "link": "/core-packages-and-features/version-control-in-pulsar",
+ "link": "/core-packages-and-features/version-control",
"summary": "Keep a history of your files’ changes."
},
{
diff --git a/docs/docs.11tydata.json b/docs/docs.11tydata.json
index ef8d5c4..332d51e 100644
--- a/docs/docs.11tydata.json
+++ b/docs/docs.11tydata.json
@@ -21,6 +21,11 @@
"link": "/customizing-pulsar",
"summary": "Learn about how to customize Pulsar’s keybindings, per-language settings, and styles."
},
+ {
+ "text": "IDE features",
+ "link": "/ide-features",
+ "summary": "Hook Pulsar up to “brain” packages that make it behave more like a language-specific IDE."
+ },
{
"text": "Developing for Pulsar",
"link": "/developing-for-pulsar",
@@ -50,11 +55,6 @@
"text": "APIs",
"link": "/api",
"summary": "Read reference documentation for our APIs: Pulsar’s own API and that of the package repository."
- },
- {
- "text": "IDE Features",
- "link": "/ide-features",
- "summary": "Hook Pulsar up to ‘brain’ packages that make it behave more like a language-specific IDE."
}
]
diff --git a/docs/ide-features/builtin-packages.md b/docs/ide-features/builtin-packages.md
new file mode 100644
index 0000000..b064947
--- /dev/null
+++ b/docs/ide-features/builtin-packages.md
@@ -0,0 +1,65 @@
+---
+title: "IDE features provided by builtin packages"
+layout: doc.ejs
+---
+
+So far we’ve told you how to install an IDE package for a given language — the “brain” that knows how to deliver lots of IDE features. But brains can’t do anything by themselves. What does the brain talk to? How do you use the features themselves?
+
+For various reasons, some of these IDE features are provided by built-in packages, and others require additional community packages to enable. First, let’s talk about the built-in packages, and the features you get for free without further installation.
+
+::: tip
+
+If any of the features described below do not work, revisit the README file for your IDE package and ensure you’ve configured it properly. See the [troubleshooting](../troubleshooting) page for more information.
+
+:::
+
+## Autocompletion
+
+We’ve learned that autocompletion is provided by the {autocomplete-plus} package. IDE packages use the `autocomplete.provider` [service](/infrastructure/interacting-with-other-packages-via-services/) to supply contextual autocompletion suggestions to the user.
+
+Even where other autocompletion providers exist — like the built-in {autocomplete-html} and {autocomplete-css} — the autocompletion you get from an IDE package is likely to be more contextually aware and more useful.
+
+
+
+
+
+
In this example, pulsar-ide-typescript is able to offer us a contextual completion for this parameter type.
+
+
+
+## Symbol resolution
+
+We briefly mentioned how you can [navigate through files by symbol](http://localhost:8082/using-pulsar/movement/#navigating-by-symbols). Those symbols could be a number of things depending on the kind of file: method names, CSS selectors, or Markdown heading names, to list a few examples.
+
+This functionality is supplied by the built-in {symbols-view} package. Even without an IDE package, Pulsar allows you to navigate by symbols in most languages for the current file. (Since the file is already open, this isn’t hard!) But `symbols-view` also has the ability to jump to a symbol in a _different_ file, but not automatically; it needs something to tell it what those symbols are. A language server can do that very thing!
+
+:::tip
+
+Old IDE packages written for Atom don’t have this feature! You’ll need an IDE provider package _specifically written for Pulsar_. Pick one [from the list](../getting-started) or ensure it starts with `pulsar-ide-` instead of just `ide-`.
+
+:::
+
+An IDE package is especially useful for symbol resolution and lets you search for symbols on a _project-wide_ basis. For instance, the **Symbols View: Toggle Project Symbols** command (Cmd+Shift+RCtrl+Shift+R by default) will open a palette into which you can type the name of a symbol that may exist anywhere in your project.
+
+
+
+
Symbols View: Toggle Project Symbols lets you search for symbols across the entire project.
+
+
+
+Even more powerful is the ability to jump to where a symbol is defined. Place your cursor inside the name of a function, then invoke the **Symbols View: Go To Declaration** command (Cmd+Alt+DownCtrl+Alt+Shift+Down by default) to jump to the declaration of that function!
+
+But this isn’t a one-way trip: when you’re done consulting the function definition, you can invoke **Symbols View: Return From Declaration** (Cmd+Alt+UpCtrl+Alt+Shift+Up by default) to return to your previous location.
+
+
+
+
Symbols View: Go To Declaration lets you jump to the place where a symbol is defined, no matter where it is in your project. Symbols View: Return From Declaration will return you to your original position.
+
+
+The list of locations you jump _from_ functions like a web browser’s history stack. For instance, you can jump three definitions deep, then invoke **Symbols View: Return From Declaration** three times in a row to go back to where you started.
diff --git a/docs/ide-features/community-packages.md b/docs/ide-features/community-packages.md
new file mode 100644
index 0000000..6b66a64
--- /dev/null
+++ b/docs/ide-features/community-packages.md
@@ -0,0 +1,162 @@
+---
+title: "IDE features provided by community packages"
+layout: doc.ejs
+---
+
+We [just showed you](../builtin-packages) a couple of features that you get for free without having to install any extra packages. Here are some others that are provided by community packages and are just a one-click install away.
+
+::: info Why aren’t these packages bundled?
+
+It depends! Here are a few reasons why:
+
+* Some of them date back to Atom days and have historically been maintained by third parties. Occasionally, such packages would be “adopted” into the fold and become bundled, as happened with {autocomplete-plus} a long time ago; but in other cases, the package maintainer may have chosen to keep their package a community package for one reason or another.
+
+* Some of these packages provide features that simply can’t be fulfilled with anything _except_ a language server, so by default they’d do nothing until the user installed an IDE package.
+
+* Some of them may _eventually_ become built-in packages, but are comparatively new and still evolving.
+
+:::
+
+::: tip Not the only game in town
+
+Some of the packages suggested below are common choices, but not the _only_ choice. Pulsar’s service-driven package architecture presents an opportunity here; since IDE packages communicate with UI packages via [services](/infrastructure/interacting-with-other-packages-via-services/), any UI package is interchangeable with any other package that consumes the same service.
+
+Even built-in packages! If you didn’t like how {autocomplete-plus} did autocompletion, you could disable it and write your own version, and it’d be able to talk to the exact same autocompletion providers as `autocomplete-plus`. For this reason, the list below is a starting point, but it is not exhaustive!
+
+:::
+
+## Linting
+
+Linting, otherwise known as code diagnostics, describes the process of flagging and annotating possible errors or mistakes in your code.
+
+A linting interface is not built into Pulsar. The canonical packages for linting are {linter} and {linter-ui-default}; `linter` was the second-most-popular community package for Atom, with millions of installations, and still works quite well in Pulsar.
+
+
+
+
+
+
The linter package shows diagnostic errors and warnings and may offer fixes.
+
+
+
+As is common with “UI” packages, the `linter` package does not do any code analysis on its own; it simply defines a system by which other packages can report code diagnostics. There are many, many packages out there that integrate into `linter` — not just IDE packages but also specialized code-linting tools. For this reason, you might already have `linter` installed!
+
+Consider perusing the lists of packages that use [the `linter` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=linter) or [the `linter-indie` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=linter); this is another way to discover IDE packages.
+
+:::note Consumers and Providers
+
+You may have noticed that the `linter` package _consumes_ the `linter` service, but _provides_ the `linter-indie` service. What’s the difference?
+
+A service sets up one or more dialogues between pairs of packages: one consumer and one provider. Whichever of the packages needs to _initiate_ communication tends to be the consumer.
+
+In this case, the services’ differing designs are the reason. The `linter` service is a “pull” service and requires the UI to ask the provider for linting errors at a time of its own choosing. The `linter-indie` service is a “push” service; as code changes, a language server will detect potential diagnostic issues and notify the UI about them.
+
+Nonetheless, this is an implementation detail that users usually won’t have to care about. All you need to know is that **providers** need to match up with **consumers** and _vice versa_.
+
+In the Pulsar package repository, each package will list the services it consumes and provides. Clicking on a particular service in that list will show you search results for packages that fulfill the _opposite_ side of that service (and can therefore be paired with the original package).
+
+:::
+
+## Code actions
+
+Language servers can do sophisticated analysis of your code and offer arbitrary options for refactors or fixes. Indeed, code actions and linting often go hand-in-hand; it’s common for a language server to both flag a possible error _and_ make suggestions on how to fix it.
+
+If you’ve used Visual Studio Code, you may be familiar with [code actions](https://code.visualstudio.com/docs/editing/refactoring) already. In Pulsar, the equivalent feature is provided by the [`intentions` package](https://web.pulsar-edit.dev/packages/intentions); a user can invoke the **Intentions: Show** command to display suggestions for the code under the cursor.
+
+
+
+
+
+
An intentions menu offering code actions to address the diagnostic error.
+
+
+You can pair up the `intentions` package with [a package that provides the `intentions:list` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=intentions:list).
+
+## Code formatting
+
+Language servers often have the ability to reformat code according to certain conventions and specifications.
+
+For instance, `clangd` has the ability to format C and C++ code in [a number of different styles](https://clang.llvm.org/docs/ClangFormatStyleOptions.html). In turn, the {pulsar-ide-clangd} package provides services like `code-format.file`, `code-format.range`, and `code-format.onSave` to offer formatting of code in different scenarios: file-wide (on demand), for the selected range of code (on demand), and whenever you save (automatically).
+
+
+
+
Reformatting a region of the buffer via pulsar-code-format.
+
+
+In Pulsar, these can be used via {pulsar-code-format} and [an IDE package that supports `code-format.range`](https://web.pulsar-edit.dev/packages?serviceType=provided&service=code-format.range) or one of the other services listed above. ([Other code-formatting packages](https://web.pulsar-edit.dev/packages?serviceType=consumed&service=code-format.range) exist, too.)
+
+## Highlighting references
+
+IDEs are smart enough to identify “references” to a given token. Imagine putting your cursor within the name of a function you’ve written and having your editor highlight all other usages of that function — in the current buffer or other open buffers. Or asking “show me everywhere this function is used in my project” and seeing a list of usages presented in a style similar to project-wide find-and-replace.
+
+
+
+
+
+
Using pulsar-find-references, other usages of editorStyles (the token under the cursor) are highlighted (and annotated in the scrollbar track).
+
+
+
+This can be done with {pulsar-find-references} and [an IDE package that supports the `find-references` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=find-references).
+
+## Renaming symbols
+
+Some people prefer language-specific IDEs over generalize text editors because they support powerful refactoring features — for instance, the ability to rename a function and have all other references to that function update in an intelligent manner across the entire project. Features like these are less error-prone than project-wide find-and-replace and allow developers to refactor code with more confidence.
+
+
+
+
Renaming a symbol in several files at once via pulsar-refactor.
+
+
+
+In Pulsar, intelligent symbol renaming can be accomplished with {pulsar-refactor} and [an IDE package that supports the `refactor` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=refactor).
+
+## Hierarchical outline
+
+We’ve seen how language servers often know about all the “symbols” (important things) in your project’s code. But that’s not all — many of them can represent those things in a hierarchical list. You can pair {pulsar-outline-view} (or [another `outline-view` consumer](https://web.pulsar-edit.dev/packages?serviceType=consumed&service=outline-view)) with [an IDE package that provides the `outline-view` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=outline-view) and enjoy these outlines in your left or right dock.
+
+
+
+
+
+
Using pulsar-outline-view, you can visualize your source files as trees, complete with collapsing/expanding of nodes.
+
+
+Even when your language server doesn’t support hierarchical symbol lists — or when an IDE package isn’t even present! — `pulsar-outline-view` can fall back to showing a flat list of symbols. You might prefer this presentation to that of **Symbols View: Toggle File Symbols**.
+
+## Hover information
+
+In other editors, you might be accustomed to hovering your mouse pointer over a given function and seeing information about it — like the parameters it takes, or an explanation of what it does.
+
+
+
+
+
+
Using pulsar-hover, contextual help is offered in overlay form for the symbol under the cursor.
+
+
+These kinds of contextual hints can be delivered with {pulsar-hover} and an IDE package that supports [the `hover` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=hover) or [the `datatip` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=datatip). ([Other hover-hint packages](https://web.pulsar-edit.dev/packages?serviceType=consumed&service=datatip) exist, too.)
+
+## Progress indicators
+
+IDE packages often perform background tasks and may want to notify the user about their progress.
+
+The {busy-signal} package offers a way to do this. It places an icon in the status bar that will animate when such a task is in progress.
+
+Pair it up with a package that consumes [the `atom-ide-busy-signal` service](https://web.pulsar-edit.dev/packages?serviceType=consumed&service=atom-ide-busy-signal).
+
+::: note
+
+A language server and its client report “capabilities” to one another when they first connect. This helps the server avoid using features that the client does not support.
+
+One such capability is the ability for the client to report progress on background tasks. When `busy-signal` is present, any IDE package that uses the `atom-languageclient` library will automatically report this capability to the language server. The language server will then know it has a way of reporting the progress of background tasks.
+
+When `busy-signal` is _not_ present, `atom-languageclient` reports a smaller set of client capabilities to the language server. Some language servers react to this by resorting to more invasive techniques for reporting progress — for instance, showing notifications.
+
+:::
diff --git a/docs/ide-features/getting-started.md b/docs/ide-features/getting-started.md
new file mode 100644
index 0000000..7871c8a
--- /dev/null
+++ b/docs/ide-features/getting-started.md
@@ -0,0 +1,93 @@
+---
+title: "Getting started"
+layout: doc.ejs
+---
+
+## What are IDE features?
+
+Definitions of “IDE” ([integrated development environment](https://en.wikipedia.org/wiki/Integrated_development_environment)) vary, but it’s common to think of an IDE as a code editor that offers deep integration with a specific language or technical stack.
+
+A cross-language editor like Pulsar has built-in support for a couple dozen languages and can be extended to support more. But “support,” in this case, means syntax highlighting, indentation hinting, code folding, and the ability to apply all these features to files of one or more specific extensions. Some language packages can also identify symbols in the current file — method names and other important sections.
+
+Pulsar needs external help for deeper integration such as the following:
+
+* Project-wide symbol identification, including jumping to a symbol’s declaration
+* Contextual autocompletion (knowing what’s valid to suggest at the cursor position for the given language)
+* Linting (code diagnostics, highlighting errors, offering suggestions)
+* Refactoring (renaming of symbols or applying “quick fix” actions)
+* Navigating code files by hierarchical “outline”
+* Intelligent code formatting
+
+But for most mainstream languages, that external help already exists!
+
+## What are language servers?
+
+A language server is a “brain” designed to supply all the logic needed to support features like those listed above.
+
+For a given language, instead of writing a specific deep integration with each commonly used code editor in the world, it’s easier to write _one_ deep integration that abides by the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/). Each editor can then write its own adapter to communicate according to that protocol.
+
+The Language Server Protocol therefore functions as “middleware”: each language community can write a language server in whatever way they please, with whatever tools they want, as long as it fulfills its end of the protocol. Likewise, every code editor can write a language client that implements the other half of the protocol, then wires it up to the specific features that editor can support.
+
+This reduces vendor lock-in and makes it easier for the Pulsars of the world to offer IDE-like features.
+
+## Why do we need language servers?
+
+Some of the features listed above have been able to be delivered through various means even _without_ language servers. For example, Pulsar has builtin packages for {autocomplete-css "CSS"} and {autocomplete-html "HTML"} autocompletion because, given a document and a cursor position, it’s comparatively easy to understand which kinds of suggestions would be contextually valid. But even these packages use techniques that aren’t as robust as what a language server can provide.
+
+Also, language servers are exciting because they offer a way for a single piece of software to meet the needs of many different IDE features at once. Lots of the IDE features mentioned above rely on the same code analysis techniques, so it only makes sense for one tool to do all the analysis within your project and deliver all these features at once.
+
+## How do I get them?
+
+Using language servers in Pulsar is a two-step process:
+
+1. First, verify that a language server exists for the language you want to use.
+2. Next, find (or create) a Pulsar package that integrates with that language server.
+
+### Find a language server for your techology stack
+
+The Language Server Project site maintains [a list of known language servers](https://microsoft.github.io/language-server-protocol/implementors/servers/) for various languages and frameworks. Often you’ll see more than one language server on that page for the same language! But it’s typically enough simply to verify that at least one server exists for your language.
+
+### Search the package repository for an IDE package for your language
+
+Packages that integrate with language servers are named according to convention. If you’re looking for an IDE package for a given language, search for `ide-[language]` in the package repository.
+
+Suppose you use a language `foo`. You may find an `ide-foo` package in the repository; this would’ve been written for Atom and may be several years old by now, but may still work to some extent.
+
+Some newer packages exist, however, that are named with `pulsar-` at the front. So if you see one called `pulsar-ide-foo`, install that instead; these tend to be IDE packages written specifically for Pulsar which have a wider set of features.
+
+In some cases, the package might specify the name of the language server rather than the name of the language. One example is `pulsar-ide-clangd`, which is named after [Clangd](https://clangd.llvm.org/), the language server for C/C++.
+
+## I use mainstream, popular languages. Can you just tell me which packages to install?
+
+Let’s make this easy for the vast majority of you:
+
+* JavaScript and [TypeScript](https://www.typescriptlang.org/) authors can install {pulsar-ide-typescript}.
+* Authors of CSS (and “enhanced” CSS languages like [Sass](https://sass-lang.com/) and [Less](https://lesscss.org/)) can install {pulsar-ide-css}.
+* Authors of HTML (and templating languages that wrap HTML — like [PHP](https://www.php.net/), [ERB](https://github.com/ruby/erb?tab=readme-ov-file#erb), and [Handlebars](https://handlebarsjs.com/guide/)) can install {pulsar-ide-html}.
+* Those who routinely write JSON can install {pulsar-ide-json}.
+* [Python](https://www.python.org/) authors can install {pulsar-ide-python}.
+* C/C++ authors can install {pulsar-ide-clangd}.
+* [Ruby](https://www.ruby-lang.org/) authors can install {pulsar-ide-ruby-solargraph}.
+* [Go](https://go.dev/) authors can install {pulsar-ide-golang}.
+* [D](https://dlang.org/) authors can install {pulsar-ide-d}.
+* [Markdown](https://www.markdownguide.org/) authors can install {pulsar-ide-markdown}.
+
+Be sure to **read the README of the package you install**. Some of these packages bundle the language server; these tend to be quicker to get up and running. Others require that you install the language server yourself and help the package find its location on your system.
+
+
+## Why don’t all these packages simply come preinstalled with Pulsar?
+
+Several reasons:
+
+* As mentioned above, they often require external tools and can’t easily be bundled within the package itself. [Clangd](https://clangd.llvm.org/) is a good example: since it must be built for your OS and processor architecture, {pulsar-ide-clangd} isn’t going to bundle it. Better for you to download it yourself and tell the package where to find the executable.
+* Packages that bundle their own language server _could_ be built into Pulsar, but they’d increase the download and installation size, so we have to be mindful of that trade-off.
+
+Still, it’s possible that some of these packages will be bundled by default in future Pulsar versions.
+
+## What if a language server exists for my language, but a Pulsar package doesn’t?
+
+If you’ve got experience with JavaScript or have read through [our package tutorials](/developing-for-pulsar/), give it a try yourself! We’ve [written a tutorial](../writing-your-own-ide-package/) for exactly this scenario.
+
+The whole point of a language server is to reduce the implementation burden on code editors, since most of the “glue” code is shared across language servers. For that reason, we maintain [a library called `atom-languageclient`](https://github.com/savetheclocktower/atom-languageclient) designed to make it easy to [connect a language server to Pulsar](https://github.com/savetheclocktower/atom-languageclient?tab=readme-ov-file#developing-packages). With practice, you can do it in about a half-hour!
+
+If you’re not comfortable writing JavaScript, [reach out to us](https://pulsar-edit.dev/community) instead. Creating an IDE package for Pulsar is easier than you think! We can often coach you through the process, or perhaps even write it for you.
diff --git a/docs/ide-features/ide-features.11tydata.json b/docs/ide-features/ide-features.11tydata.json
new file mode 100644
index 0000000..b0cfb12
--- /dev/null
+++ b/docs/ide-features/ide-features.11tydata.json
@@ -0,0 +1,30 @@
+{
+ "indexTitle": "IDE features",
+ "sidebar": [
+ {
+ "text": "Getting started",
+ "link": "/ide-features/getting-started",
+ "summary": "What are “IDE features”? How can they improve your editor experience?"
+ },
+ {
+ "text": "IDE features provided by builtin packages",
+ "link": "/ide-features/builtin-packages",
+ "summary": "Learn which IDE features work out of the box."
+ },
+ {
+ "text": "IDE features provided by community packages",
+ "link": "/ide-features/community-packages",
+ "summary": "Learn which IDE features require the installation of additional packages."
+ },
+ {
+ "text": "Writing your own IDE package",
+ "link": "/ide-features/writing-your-own-ide-package",
+ "summary": "Useful information for package developers who want to wrap language servers."
+ },
+ {
+ "text": "Troubleshooting",
+ "link": "/ide-features/troubleshooting",
+ "summary": "Learn how to diagnose the problem when IDE features aren't working."
+ }
+ ]
+}
diff --git a/docs/ide-features/index.md b/docs/ide-features/index.md
new file mode 100644
index 0000000..10c0dde
--- /dev/null
+++ b/docs/ide-features/index.md
@@ -0,0 +1,20 @@
+---
+title: "IDE features"
+layout: summary.ejs
+---
+
+Out of the box, Pulsar is an excellent and versatile code editor. But it’s also capable of being a deeply _wise_ code editor for a number of different languages.
+
+If your favorite language has a [_language server_](https://en.wikipedia.org/wiki/Language_Server_Protocol) — a tool designed to support common editor tasks for that language — then Pulsar can embrace that language server in order to improve your editing experience by adding a number of features.
+
+We call these **IDE features** because they’re the kinds of features you typically get in a single-purpose [integrated development environment](https://en.wikipedia.org/wiki/Integrated_development_environment) than from a general-purpose code editor:
+
+ * [Autocompletion](/ide-features/builtin-packages/#autocompletion)
+ * [Project-wide symbol search and “Jump to declaration” functionality](/ide-features/builtin-packages/#symbol-resolution)
+ * [Linting and code diagnostics](/ide-features/community-packages/#linting)
+ * [Identifying and highlighting references](/ide-features/community-packages/#highlighting-references)
+ * Refactoring (for example, [renaming a method project-wide](/ide-features/community-packages/#renaming-symbols))
+ * [A hierarchical outline of the current file](/ide-features/community-packages/#hierarchical-outline)
+ * [Rich information on hover](/ide-features/community-packages/#hover-information), like method signatures and links to documentation
+
+Read further in this section to find out how to enable these features.
diff --git a/docs/ide-features/troubleshooting.md b/docs/ide-features/troubleshooting.md
new file mode 100644
index 0000000..aebd6c1
--- /dev/null
+++ b/docs/ide-features/troubleshooting.md
@@ -0,0 +1,71 @@
+---
+title: "Troubleshooting"
+layout: doc.ejs
+---
+
+IDE packages add more features to Pulsar, but they also introduce new possible points of failure. Here are a few techniques you can use to diagnose these failures.
+
+## How do I figure out why something isn’t working?
+
+IDE packages tend not to have interfaces, so troubleshooting can sometimes be tricky. The `README` file of the specific IDE package is the best place to start. Ideally, it should have:
+
+* The name of the underlying language server and a link to its own documentation
+* Information about the configuration options available for that package
+
+### How failures happen
+
+Here are a few things that could be going wrong:
+
+1. As stated earlier, some language servers will have dependencies that are not included in the package itself. Re-read the documentation and make sure you’ve got everything installed that you need to, and that your IDE package can see whatever you’ve installed (in other words, make sure any new binaries you installed are in your `PATH`).
+
+2. Sometimes a language server requires extra amounts of hand-holding or configuration. For instance, the `clangd` language server that powers {pulsar-ide-clangd} (for C/C++) wants to find [a file called `compile_commands.json`](https://clangd.llvm.org/installation#compile_commandsjson) somewhere within your project; this file is typically generated by your build system.
+
+ Similarly, a language server might be a thin wrapper around other tools with their own conventions. One example is python’s `pylsp`; some of its features are enabled by default, but others require [opt-in via configuration](https://github.com/python-lsp/python-lsp-server?tab=readme-ov-file#configuration). If it feels like your IDE package isn’t providing as many features as it seems like it should, this may be one reason why.
+
+3. Some IDE packages (especially older ones designed for Atom) may not be keeping up with their underlying language servers, and may be advertising fewer features than those language servers can support. If an IDE package hasn’t been updated in a while and you think it’s not being actively maintained, ask about it in one of our communities and we’ll take a look; it might just need a version bump or some new metadata!
+
+### Using `symbols-view` as a guinea pig
+
+Most language servers support symbol resolution, so the built-in {symbols-view} package has some tools that can often help you diagnose whether your language server is working properly.
+
+::: warning
+
+This won’t work with older IDE packages (the ones whose names start with `ide-`), since they were written before `symbols-view` had the ability to use other packages as data providers. But it should work with [any IDE package that provides the `symbol.provider` service](https://web.pulsar-edit.dev/packages?serviceType=provided&service=symbol.provider).
+
+:::
+
+For instance: invoking a command called **Symbols View: Show Active Providers** will display a notification with a list of all known symbol providers, along with the packages that provide them.
+
+
+
+Two of these providers, **ctags** and **Tree-sitter**, are built into Pulsar. The others in the list are provided by community packages and IDE packages in particular. If you don’t see your IDE package in this list, there’s a good chance it’s failing to start properly; read below.
+
+If your package is listed in the notification, you can go to the `symbols-view` settings and select the **Show Provider Names In Symbols View** setting. This will add a badge onto palette items in symbols lists that shows the source of the symbol.
+
+
+
+If an IDE package is providing symbols, it’s typically regarded as a better source of information than the built-in symbol providers. So if it’s working properly, you should expect it to “win” over such providers.
+
+## What if I just want to look at logs?
+
+You should [open your developer tools](/troubleshooting-pulsar/checking-for-errors-in-developer-tools/) and look through the console for any errors related to your package. If the package fails to activate, there will typically be a message indicating it.
+
+If the package seems to activate properly — for instance, if you can view its package settings page — then the underlying language server is probably failing to start up. Language servers launch in a separate process from Pulsar; if that launch process fails for whatever reason, the “brain” of the IDE package will be absent.
+
+Luckily, all IDE packages based on `atom-languageclient` have a hidden logging mode. You can enable it by opening your developer tools, focusing the Console tab, and executing this JavaScript:
+
+```js
+atom.config.set('core.debugLSP', true);
+```
+
+Once you change this setting, reload your window via the **Window: Reload** command. Open developer tools again and you should see some new log lines. Each language-server–related log message should be annotated with the name of the server, and you should even be able to filter on that name to look at the output from one specific IDE package:
+
+
+
+Here you can see all the ways that the IDE package (the language client) is communicating with the language server. If something is going wrong during startup, this logging should make it easier to identify the root cause.
+
+When you’re done drinking from the firehose, you can disable the debug logging and reload your window once again:
+
+```js
+atom.config.set('core.debugLSP', false);
+```
diff --git a/docs/ide-features/writing-your-own-ide-package.md b/docs/ide-features/writing-your-own-ide-package.md
new file mode 100644
index 0000000..1956e52
--- /dev/null
+++ b/docs/ide-features/writing-your-own-ide-package.md
@@ -0,0 +1,341 @@
+---
+title: Writing your own IDE package
+layout: doc.ejs
+---
+
+Pretend you’re the inventor of a new language called Zzyzx.
+
+Because you want to get Zzyzx support into as many editors as possible with as little effort as possible, you understand that you need to write a language server for Zzyzx. Armed with your computer and [the LSP specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/), you lock yourself in a room and emerge with version 1.0 of `zzyzx-language-server` a week later.
+
+Now what?
+
+The “last mile” of integration varies with each editor, but your favorite editor is Pulsar, so obviously you’ll want to start there. Here’s how to write an IDE package for your brand new language.
+
+## `atom-languageclient`
+
+The `atom-languageclient` package originated on the Atom team sometime around 2017. Later, it was adopted by the `atom-community` project, and now Pulsar uses [its own fork](https://github.com/savetheclocktower/atom-languageclient).
+
+It’s not a Pulsar package; you don’t install it from Pulsar yourself. But it’s a package meant to be used _as a dependency_ of a Pulsar package. It cuts down on almost all the boilerplate of integrating with a language server and makes it so that you can write a new integration in an hour or less.
+
+### What do I get for free?
+
+Let’s think about this from the perspective of the features we want, then work backwards until we reach the language server that can provide the information we need.
+
+* We know that we have a package called {autocomplete-plus} that can offer autocompletion for anything that implements the `autocomplete.provider` service.
+* We also have a language server that can offer autocompletion options for anything that can make a [`textDocument/completion` request](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion) — the standardized way for a lanuage server to communicate about autocompletion.
+* We therefore need the IDE package to speak LSP to the language server, then translate it to `autocomplete.provider` terminology for {autocomplete-plus}.
+
+The same need exists across other IDE features:
+
+* Language servers have methods like [`textDocument/documentSymbol`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) and [`workspace/symbol`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol) that will provide symbol information; the IDE package just needs to translate these responses to `symbol.provider` terminology.
+* Language servers can “push” diagnostic information to a language client via the [`textDocument/publishDiagnostics`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_publishDiagnostics) as the user types in a file. The IDE package must translate these messages to the `linter-indie` service format so they can be consumed by the {linter} package.
+
+The vast majority of this “glue” code is not specific to any one language server! If a language server abides by the spec, most of this work will already be handled by `atom-languageclient`. Indeed, most IDE packages are thin wrappers around `atom-languageclient`.
+
+### What don’t I get for free?
+
+Language servers are similar, but they’re not identical to one another. There will be a small amount of extra code you’ll have to write on top of what `atom-languageclient` gives you for free:
+
+* It doesn’t know how to launch your specific language server — what it’s called, whether it runs in a Node process or not, or its communications strategy. It relies on you to tell it these things.
+* It doesn’t know the name of your language or your language server.
+* It doesn’t know anything about the configuration format of your language server. The LSP specification [allows the client to provide configuration data to the server](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_configuration), but the format of this data is left up to the individual server. You may want to add configuration to your IDE package that manages the configuration object that gets sent to the server.
+* It doesn’t know how to identify which files should trigger it. By default, `atom-languageclient` doesn’t spawn a language server as soon as you launch Pulsar; it waits until you’ve got a relevant kind of file open for editing. (For instance, `pulsar-ide-typescript` won’t run a language server until you open a TypeScript or JavaScript file.)
+
+ Rather than identify these languages by file extension (which can be ambiguous and doesn’t do anything for brand-new files), it identifies them by the grammar you assign — and grammars are identified by their root scopes. For `pulsar-ide-typescript`, those scopes are `source.js`, `source.ts`, and `source.ts.tsx`. Your IDE package probably works on a fixed subset of grammars, but there are ways to expand this list and make it user-configurable.
+
+* Some IDE packages try to improve the “onboarding” experience for new installations. For instance, if the package is unable to bundle the language server, it might show a notification on first run offering a link to where the language server can be downloaded. This is outside the scope of what `atom-languageclient` does, so if you want a feature like this, you’ll need to write it yourself.
+
+## Writing your package
+
+::: info
+
+We have no clue which language you’re trying to write an IDE package for, so let’s just pretend it’s our fictional language called **Zzyzx**. Whenever you read “Zzyzx,” insert the language you’re actually working with!
+
+Let’s further assume that a language package for Zzyzx already exists, and that it provides a Zzyzx grammar for syntax highlighting and whatnot. Much like the root “scope name” is `source.js` for JavaScript files and `source.python` for Python files, we’ll assume the root scope name for the Zzyzx grammar is `source.zzyzx`.
+
+:::
+
+### Bootstrap
+
+Generate an ordinary package just like you did when you wrote [your first Pulsar package](https://docs.pulsar-edit.dev/developing-for-pulsar/package-word-count/#title). Call it `pulsar-ide-zzyzx`.
+
+### Add `atom-languageclient`
+
+Pulsar will open a new project window for `pulsar-ide-zzyzx`. But first let’s go into the terminal to the root of the folder where you installed it. Run:
+
+```
+npm install @savetheclocktower/atom-languageclient
+```
+
+This installs Pulsar’s fork of `atom-languageclient`.
+
+Let’s pretend that the Zzyzx language server is written in Zzyzx and runs as a standalone executable called `zzyzx-language-server`. You must download it yourself and ensure that it is somewhere on your `PATH` so that you can just type `zzyzx-language-server` in your terminal and have it start.
+
+::: tip
+
+Once you get the basic stuff working, you might want to introduce a configuration option to let the user specify an absolute path to their `zzyzx-language-server` binary.
+
+:::
+
+### Subclass `AutoLanguageClient`
+
+Delete the existing `.js` files in `lib`, then create a new file called `main.js`. In your `package.json`, make sure the `main` field points to `./lib/main`.
+
+Make your `main.js` look like this:
+
+```js
+const { AutoLanguageClient } = require('@savetheclocktower/atom-languageclient');
+
+class ZzyzxLanguageClient extends AutoLanguageClient {
+ getGrammarScopes() {
+ // The root language scope for Zzyzx files in Pulsar.
+ return ["source.zzyzx"];
+ }
+ getLanguageName() {
+ // What do you call your language?
+ return "Zzyzx";
+ }
+ getServerName() {
+ // What do you call this server?
+ return "Zzyzx Language Server";
+ }
+ getPackageName() {
+ // What's the name of this package?
+ return "pulsar-ide-zzyzx";
+ }
+ startServerProcess (projectPath) {
+ return super.spawn(
+ // The hypothetical name of your language server's executable.
+ "zzyzx-language-server",
+ // Any other arguments that the server expects.
+ [],
+ // Options to pass to the `spawn` command (the same ones
+ // `child_process.spawn` would expect in Node). Most language servers
+ // expect that they can treat the working directory as the project root,
+ // so that's what we'll do.
+ { cwd: projectPath }
+ );
+ }
+}
+
+module.exports = new ZzyzxLanguageClient();
+```
+
+This is pretty easy! All you’re doing is creating a subclass of the `AutoLanguageClient` class, and that’s the class that does most of the work. It only needs you to tell it the things that it’s not able to guess on its own, like how to run your server and what the server is called.
+
+### Expose some services
+
+For an IDE package to be able to talk to UI packages, it has to [advertise the services](/infrastructure/interacting-with-other-packages-via-services/) it’s fluent in. Open up your `package.json` again.
+
+Before we add the services, though, we should make this change: your `package.json` probably has an `activationCommands` field. Remove it and insert this in its place:
+
+```json
+"activationHooks": [
+ "source.zzyzx:root-scope-used"
+],
+```
+
+Earlier we mentioned that the language server won’t start until we have a Zzyzx file open in our editor. But we can go further than that; this metadata tells Pulsar, “you don’t actually have to _activate this package_ immediately on launch — you can wait until the user opens their first Zzyzx file!”
+
+As for the services: [this whole block of advertised services](https://github.com/savetheclocktower/pulsar-ide-typescript/blob/321a7713b521c12a9475f7d1bacb024f536433d9/package.json#L299-L399) from `pulsar-ide-typescript` is a good one to borrow. It boasts support for pretty much everything that `atom-languageclient` can theoretically support. If your language server doesn’t actually support some of these features, that’s fine; the service provider will fail silently.
+
+If you have a good sense of what your language server does and does not support, you can pare down this list as needed.
+
+### Test it out
+
+Save your source files, reload your window (make sure your window is open in [dev mode](https://docs.pulsar-edit.dev/contributing-to-pulsar/hacking-on-the-core/#running-in-development-mode)), and try writing some Zzyzx. You should see evidence of IDE features being provided! Write part of a token and see if it gets autocompleted. Open the symbols palette and see if you can spot any new entries.
+
+You can also invoke the **Symbols View: Show Active Providers** command from your command palette to see a list of symbol providers. One of them should be called _Zzyzx Language Server_. If it isn’t there, chances are good that there’s an error somewhere preventing the package from activating properly; [open your developer tools and look for errors](/troubleshooting-pulsar/checking-for-errors-in-developer-tools/).
+
+If this doesn’t work, turn on logging [as described on the troubleshooting page](/ide-features/troubleshooting/#what-if-i-just-want-to-look-at-logs%253F) and see if the logs shed any light.
+
+## Customization via overrides
+
+The fact that `AutoLanguageClient` does so much of the work may make it seem like you don’t have much of a say in how your package behaves. But there are many ways to customize language server responses!
+
+Above we saw the methods that you are _required_ to implement in your subclass order for `AutoLanguageClient` to do its work. But there are many other methods that have default implementations, but are meant to be overridden if you want your package to behave differently.
+
+Here’s a non-exhaustive list of things you can customize. For the full list, [read the `AutoLanguageClient` source code](https://github.com/savetheclocktower/atom-languageclient/blob/master/lib/auto-languageclient.ts).
+
+```js
+class ZzyzxLanguageClient extends AutoLanguageClient {
+ // (all the methods from before, plus…)
+
+ // CORE OVERRIDES
+ // ==============
+
+ // You can customize the logic that decides when to spawn a language server
+ // if one isn't already running.
+ shouldStartForEditor(textEditor) {
+ let shouldStart = super.shouldStartForEditor(textEditor);
+ // By default it compares the editor's grammar to the list of scope names
+ // from before. Here, though, you could impose additional requirements,
+ // like the presence of a configuration file.
+ return atom.project.getDirectories().some((directory: Directory) => {
+ // We'll check for a file called `.zzyzx-config` in each of the project
+ // roots; if one doesn't exist, we won't start the server.
+ let configFile = directory.getFile('.zzyzx-config');
+ return configFile.existsSync();
+ });
+ }
+
+ // You can customize the data that is sent to the language server during
+ // initialization.
+ getInitializeParams(projectPath, languageServerProcess) {
+ let result = super.getInitializeParams(projectPath, languageServerProcess);
+ // Language servers wait for the client to report its own capabilities,
+ // then behave accordingly. They won't try to do things for which the
+ // client doesn't advertise support. In some cases you can use this to "opt
+ // out" of features that you might not want from the server for one reason
+ // or another (like configurability).
+ //
+ // Here's an example from real life: a language server wants some way of
+ // reporting the progress of a long-running task. That's why we use
+ // `busy-signal`, but if that package isn't installed, we don't advertise
+ // support for this capability, so a server might fall back to a more
+ // invasive method of reporting progress — like showing notifications.
+ //
+ // One way around this is to opt into the `window.workDoneProgress`
+ // capability whether or not we know how to handle it.
+ result.window.workDoneProgress = true;
+ return result;
+ }
+
+ getConnectionType() {
+ // `atom-languageclient` supports three methods of communicating with the
+ // language server process: `socket`, `stdio`, and `ipc`. You may force
+ // a specific type here.
+ return 'stdio';
+ }
+
+ // CORE CONFIGURATION
+ // ==================
+
+ // If you like, you can formally link your IDE package's configuration schema
+ // to that of your language server. One approach would be to define a special
+ // section in your schema so that only a subset of your settings are used.
+ //
+ // If this system is used, `AutoLanguageClient` will observe this key path
+ // and send a new configuration immediately if the user changes any settings
+ // while the language server is running.
+ getRootConfigurationKey(): string {
+ return `${this.getPackageName()}.serverSettings`
+ }
+
+ // By default, whatever gets returned above would be sent to the server as
+ // configuration. In this case, it would be the return value of:
+ //
+ // atom.config.get('pulsar-ide-zzyzx.serverSettings');
+ //
+ // But you can, if necessary, transform the configuration object before
+ // sending it to the server.
+ //
+ mapConfigurationObject(configuration) {
+ return configuration
+ }
+
+ // CORE HOOKS
+ // ==========
+
+ preInitialization(languageClientConnection) {
+ // Act after a language server is spawned but before the client reports its
+ // capabilities.
+ }
+
+ postInitialization(server) {
+ // Act after the language server reports its capabilities to the server.
+ // This hook is sometimes used to send configuration or call custom methods
+ // that aren't in the LSP spec.
+ }
+
+ // FILTERING
+ // =========
+
+ // Sometimes a language server considers _practically anything_ to be a
+ // symbol. One way to filter that list is to implement this method and have
+ // it ignore symbols that aren't important.
+ shouldIgnoreSymbol(symbol) {
+ return symbol.tag === 'variable'
+ }
+
+
+ // Similarly, you have an opportunity to ignore certain diagnostic messages
+ // before they are handled by the `linter` package.
+ shouldIgnoreMessage(diagnostic, textEditor, range) {
+ // Diagnostic messages often have an associated severity: error, warning,
+ // info, or hint. Let's ignore hints.
+ return diagnostic.severity === 4;
+ }
+
+ // And you can also filter code actions before they're shown in an
+ // `intentions` menu.
+ filterCodeActions(commandsOrCodeActions) {
+ // Not the most elegant example, but this would be one way of disabling
+ // code actions altogether.
+ if (!atom.config.get(`${this.getPackageName()}.codeActions.enable`)) {
+ return null
+ }
+ }
+
+ // PRIORITY
+ // ========
+
+ // When more than one provider is available for a certain feature, UI
+ // packages often resolve this with a "priority" system: each provider
+ // reports its own priority score, and the highest number wins.
+ //
+ // This allows for some amount of customization of which provider gets picked
+ // in certain scenarios. The default return value of this method is `1`, but
+ // you can instead expose it to the user to customize.
+ getPriorityForHover() {
+ let setting = atom.config.get(`${this.getPackageName()}.hover.priority`)
+ return setting
+ }
+
+ getPriorityForSignatureHelp() {
+ // (et cetera)
+ }
+
+ getPriorityForCodeFormat() {
+ // (et cetera)
+ }
+
+ // HOOKS
+ // =====
+
+ // This function is called _before_ a given code action is executed. This
+ // gives you a chance to prevent the code action or allow it to proceed. You
+ // can even go async if you need to, though it's best for the user if you
+ // don't go _too_ async.
+ async onApplyCodeActions(commandOrCodeAction) {
+ if (await someDisruptiveTaskIsOngoing()) {
+ return false
+ }
+ return true
+ }
+
+ // This function is called immediately after an autocompletion suggestion is
+ // inserted. It gives you a chance to act before the UI updates.
+ //
+ // One contrived example: suppose your language server inserts text
+ // incorrectly in certain scenarios and you'd like to work around it until
+ // the bug is fixed upstream.
+ onDidInsertSuggestion(suggestionInsertedEvent) {
+ let { editor, triggerPosition, suggestion } = suggestionInsertedEvent;
+
+ if (suggestion.text) {
+ let range = new Range(
+ triggerPosition,
+ triggerPosition.traverse([0, suggestion.length])
+ );
+ let originalText = editor.getTextInRange(range);
+ let fixedText = fixBugInLanguageServerAutocompletion(originalText);
+ editor.setTextInRange(range, fixedText);
+ }
+ }
+}
+```
+
+## I’m stuck!
+
+We love IDE packages and want to make their authorship as headache-free as possible. If you get stuck, [reach out via a community channel](https://pulsar-edit.dev/community/) and we’ll help you figure it out.
diff --git a/docs/main.js b/docs/main.js
index 3cff968..c5ed3c2 100644
--- a/docs/main.js
+++ b/docs/main.js
@@ -349,6 +349,7 @@ const Tabs = {
}
const Hovercards = {
+ failedRetrievals: new Set(),
setup () {
document.body.addEventListener('click', this.onClick.bind(this));
@@ -374,6 +375,11 @@ const Hovercards = {
let value = node.dataset.hovercard;
if (!value) return;
+ if (this.failedRetrievals.has(value)) {
+ // We failed on a previous attempt, so let's not try again.
+ return
+ }
+
let targetRect = node.getBoundingClientRect();
let bodyRect = document.body.getBoundingClientRect();
@@ -381,6 +387,11 @@ const Hovercards = {
let left = Math.abs(bodyRect.left) + targetRect.left + targetRect.width;
const res = await fetch(`/hovercards/${value}.json`);
+ if (!res.ok) {
+ // We didn't fetch a hovercard, so we'll remember not to try again.
+ this.failedRetrievals.add(value)
+ return
+ }
const card = await res.json();
if (card.empty) return;
diff --git a/helpers/index.js b/helpers/index.js
index 47b8487..15c94c0 100644
--- a/helpers/index.js
+++ b/helpers/index.js
@@ -6,7 +6,7 @@ const semver = require("semver");
module.exports = {
nextAndPreviousSidebarEntries(url, sidebar) {
let prev = null, next = null;
- if (!url) return [prev, next];
+ if (!url || !sidebar) return [prev, next];
let lastIndex = sidebar.length - 1;
let index = sidebar.findIndex(entry => entry.link === url || `${entry.link}/` === url);
if (index > -1) {
diff --git a/img/atom/autocomplete-plus-language-server.png b/img/atom/autocomplete-plus-language-server.png
new file mode 100644
index 0000000..9fb5aa1
Binary files /dev/null and b/img/atom/autocomplete-plus-language-server.png differ
diff --git a/img/atom/code-actions.png b/img/atom/code-actions.png
new file mode 100644
index 0000000..f1d0467
Binary files /dev/null and b/img/atom/code-actions.png differ
diff --git a/img/atom/code-format.webm b/img/atom/code-format.webm
new file mode 100644
index 0000000..7486295
Binary files /dev/null and b/img/atom/code-format.webm differ
diff --git a/img/atom/ide-log-output.png b/img/atom/ide-log-output.png
new file mode 100644
index 0000000..d91da17
Binary files /dev/null and b/img/atom/ide-log-output.png differ
diff --git a/img/atom/linter.png b/img/atom/linter.png
new file mode 100644
index 0000000..2bf989b
Binary files /dev/null and b/img/atom/linter.png differ
diff --git a/img/atom/pulsar-find-references.png b/img/atom/pulsar-find-references.png
new file mode 100644
index 0000000..2f35404
Binary files /dev/null and b/img/atom/pulsar-find-references.png differ
diff --git a/img/atom/pulsar-hover.png b/img/atom/pulsar-hover.png
new file mode 100644
index 0000000..253b774
Binary files /dev/null and b/img/atom/pulsar-hover.png differ
diff --git a/img/atom/pulsar-outline-view.png b/img/atom/pulsar-outline-view.png
new file mode 100644
index 0000000..0de5fa3
Binary files /dev/null and b/img/atom/pulsar-outline-view.png differ
diff --git a/img/atom/pulsar-refactor.webm b/img/atom/pulsar-refactor.webm
new file mode 100644
index 0000000..29afc49
Binary files /dev/null and b/img/atom/pulsar-refactor.webm differ
diff --git a/img/atom/symbols-view-go-to-declaration.webm b/img/atom/symbols-view-go-to-declaration.webm
new file mode 100644
index 0000000..c523b6d
Binary files /dev/null and b/img/atom/symbols-view-go-to-declaration.webm differ
diff --git a/img/atom/symbols-view-provider-names.png b/img/atom/symbols-view-provider-names.png
new file mode 100644
index 0000000..5c7d5fe
Binary files /dev/null and b/img/atom/symbols-view-provider-names.png differ
diff --git a/img/atom/symbols-view-providers.png b/img/atom/symbols-view-providers.png
new file mode 100644
index 0000000..cc53620
Binary files /dev/null and b/img/atom/symbols-view-providers.png differ
diff --git a/img/atom/symbols-view-toggle-project-symbols.webm b/img/atom/symbols-view-toggle-project-symbols.webm
new file mode 100644
index 0000000..99f626d
Binary files /dev/null and b/img/atom/symbols-view-toggle-project-symbols.webm differ
diff --git a/layouts/api/page.ejs b/layouts/api/page.ejs
index b40ab80..1404e46 100644
--- a/layouts/api/page.ejs
+++ b/layouts/api/page.ejs
@@ -1,56 +1,57 @@
-<%- include("header") %>
-