GNU General Public License v3.0

技术解读

以下内容由 GitRead 中文社区 编辑团队基于项目 README 与官方资料整理, 结合中文开发者的常见使用场景补充技术选型建议。具体以项目官方文档为准。

DCD:D 语言的自动完成功能守护

项目背景与痛点

D 语言以其高性能和全方位的特性成为开发者的另一选择,但在传统的开发环境中,它却缺乏类似其他语言所拥有的丰富自动完成功能。自动完成功能可以显著提升编码效率和减少出错概率,因此为 D 语言引入这样一种自动完成功能显得尤为必要。

DCD(D Completion Daemon)项目就是为了解决这一痛点而生。它作为 D 语言环境自动完成功能的守护者,通过实时解析代码,为开发者提供及时且准确的代码补全建议,从而解决了 D 语言开发者长期以来面临的编码体验不佳的问题。

核心技术揭秘

DCD 的核心在于其实时解析引擎以及高效的算法。D 语言的语法特点以及编译周期较长的特点,为其实现带来了不少挑战。DCD 的设计思想紧紧抓住了现代开发工具对效率的需求,它采用守护进程的方式实时解析代码,为开发者提供准确的补全建议。

具体来看,DCD 使用了一个异步的数据流模型来保证实时解析与响应:

graph TD I1[用户输入代码] --> I2{"代码变化检测"} I2 -->|检测到变化| I3[进程通知] I3 --> I4{"语法检测"} I4 -->|有错误| I5[显示错误信息] I4 -->|无错误| I6[部分解析] I6 --> I7[索引代码库] I7 --> I8[获取上下文] I8 --> I9[生成补全建议] I9 --> I10{"显示建议?"} I10 -->|显示| I11[前端显示] I10 -->|不显示| I12[继续监听]

从上图可以看出,DCD 的设计充分利用了异步特性来处理代码解析与补全请求,这样可以保证在不影响编译器解释解析大文件的情况下,快速给出代码补全建议。

此外,DCD 在设计过程中还引入了缓存机制,以减少重复解析相同代码段造成的耗时。通过这种设计,DCD 可以为开发者提供流畅高效的代码补全体验。

功能亮点与差异

对比其他语言的自动完成功能实现,DCD 独有的后台守护进程模式显著提高了效率,减少了对开发者的打扰。它能够在代码输入时进行实时解析,而无需对整个开发环境进行全局解析,这使其在性能上与竞争产品拉开差距。更重要的是,DCD 的实现使用了 D 语言本身的库,这使其在保持高效的同时,也更好地融入了 D 语言的生态系统。

除了核心的自动完成功能,DCD 还支持变量补全、函数补全,以及支持文档和示例的展示,这进一步提升了开发者在编码过程中的体验。

应用场景与落地建议

在生产环境中使用 DCD,需要注意以下几点:

  1. 兼容多种编辑器:确保所使用的编辑器支持 DCD 插件。
  2. 减少编译器干扰:在补全建议的生成中,适时关闭后台干扰较大的编译器。
  3. 缓存管理:优化缓存策略,减少重复解析所造成的性能浪费。

DCD 适用于任何使用 D 语言进行开发的场景,尤其是在需要快速开发并频繁编码的情况下。它可以帮助开发者迅速生成核心代码框架,减少编码错误,并有效提升整体开发效率。

综合评价

DCD 值得成为 D 语言开发者的开发工具之一,其迅速的响应与准确的建议很好地满足了开发效率的需求。同时,部分特定场景的优化工作(如多编辑器兼容性改进)仍然有待完善,如果对功能的进一步优化加以完善,DCD 无疑将在 D 语言开发社区发挥更大的作用。

简言之,DCD 的引入无疑为 D 语言开发者提供了一款强大的辅助工具。通过其实时解析与高效补全能力,DCD 能够显著提升编码效率,是现代 D 语言编程环境中不可或缺的一部分。

关于

GitHub 原始描述(英文)

The D Completion Daemon is an auto-complete program for the D programming language

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

简要分析

热度分
545
价值分
243
活跃状态
活跃

语言占比

Batchfile
D
DTrace
Makefile
Meson
NSIS
Python
Roff
Shell
TypeScript

Release

README

DCD

The D Completion Daemon is an editor-first auto-completion engine for the D programming language. The engine favors speed, whereas precision will be incrementally improved with time.

Added LSP support

DCD’s LSP server is deliberately lean it is a thin protocol layer over the same semantic engine that powers the classic socket mode, with no separate client process, no socket hop, and no editor-specific plugins. The editor talks straight to the engine over stdio, which keeps startup fast, latency low, and the feature set focused on what DCD does well.

Status. The core feature set works and is covered by automated tests (completion, hover, go-to-definition, references, rename, signature help, document symbols, inlay hints, automatic import-path detection). Not yet implemented: incremental document sync, workspace/symbol, formatting, and completionItem/resolve. The semantic engine is shared with the classic socket mode, so engine-level limitations are identical.

The server now speaks the Language Server Protocol directly:

dcd-server --lsp

Any LSP-capable editor (VS Code, Neovim, Emacs, Kate, Helix, and many more) gets DCD support with zero editor-specific code, instead of every editor needing its own plugin for DCD’s custom socket protocol. Being lean also removes a layer: the editor talks straight to the semantic engine over stdio, with no client process or socket hop in between, which lowers latency.

See editors/code/INSTALL.md for the full installation guide (building the server, the VS Code extension, and wiring up Neovim, Helix, Emacs, Kate, and other editors).

Development

Building

The LSP server is part of the regular dcd-server binary:

dub build --config=server

This produces bin/dcd-server (add --build=release for an optimized build). The client builds the same way with --config=client.

Testing

Two Python scripts drive the LSP server over stdio and exercise the full protocol (lifecycle, completion, hover, definition, references, document symbols, inlay hints):

python3 lsp-tests/lsp_smoke_test.py     # protocol lifecycle + basic requests
python3 lsp-tests/lsp_semantic_test.py  # semantic correctness (real symbols, positions)

Both must pass before committing changes to src/dcd/server/lsp/.

The classic socket mode has its own suite: tests/run_tests.sh.

VS Code extension

A fully working extension lives in editors/code/. It is a thin TypeScript client around vscode-languageclient; the interesting logic is all in the server, so the extension stays small and easy to audit.

./editors/code/install.sh

rebuilds the server (dub), compiles the extension (tsc), packages a .vsix, and installs it into VS Code in one step. Useful flags:

  • --fast: skip the dub rebuild, reuse the existing bin/dcd-server
  • --tests: also run the LSP test suites

editors/code/test_extension.sh runs a deeper end-to-end check (build → package → install → live completion against Phobos).

Source layout

  • src/dcd/server/lsp/: the LSP server: jsonrpc.d (framing), protocol.d (types), document.d (sync + position conversion), handlers.d (request handlers), lsp_server.d (lifecycle state machine)
  • src/dcd/server/autocomplete/: the semantic engine shared by both the socket and LSP modes
  • editors/code/: the VS Code extension

Status

This program is reasonably stable. Please report problems on the Github issue tracker. Please be sure that you have read the documentation before filing an issue. (If you want to help your bug to get fixed faster, you can create a test case that helps isolate the issue.)

  • Working:
    • Completion of built-in type properties, enums, and class/struct/interface instances
    • Call tips for functions, constructors, and function-type variables, with doc comments
    • import statement completions, including public imports
    • alias declarations, alias this, and with statements
    • __traits, scope, and extern arguments
    • Finding the declaration location of the symbol at the cursor
    • UFCS suggestions for most receiver shapes, including chains (obj.method().)
    • String mixins generated by simple eponymous templates (partial support, see below)
    • Type inference: auto declarations from initializers, foreach loop variables (including function call results)
    • Context-aware completion: struct initializer field names, named arguments (foo(al offers parameter names), parameter names at bare argument positions (LSP), switch case labels, statement/declaration keywords (as offers assert)
  • Not working:
    • Some UFCS edge cases (e.g. indexing a call result, nested receivers)
    • Evaluating template constraints for UFCS (a constrained template may be suggested even where its constraint would fail)
    • Template parameter deduction for UFCS beyond the bare parameter (e.g. void f(T)(T* p) is not matched against an int* receiver)
    • Autocompletion of declarations with template arguments (no T → int substitution)
    • Determining the type of an enum member when no base type is specified, but the first member has an initializer
    • auto functions (which can then propagate the failure to auto declarations)
    • That one feature that you REALLY needed

String mixins (partial support, new)

DCD does not run a CTFE engine, but it evaluates the common pattern of string mixins generated by simple eponymous templates — when the argument can be computed from string literals, ~ concatenation, and template value parameters, the generated declarations become real symbols (completion, go-to-definition, hover all work):

enum GenStruct(string name) = "struct " ~ name ~ ";";
mixin(GenStruct!q{MyType});   // MyType becomes a real symbol

Also supported: all string literal forms, alias forwarders (alias Fwd(string name) = GenStruct!name;), and mixins nested in the generated code (recursion-capped). Not supported: arbitrary compile-time function calls, format, __traits compositions, or anything needing real CTFE.

Go-to-definition on a generated symbol points at the mixin(...) site.

Setup

General

  1. Install a recent D compiler. DCD is tested with DMD 2.068.2, DMD 2.069.0-rc2, and LDC 0.16 (Do not use DMD 2.068.1)
  2. Follow the directions listed below for Homebrew, Git + Make, or Dub, depending on how you would like to build DCD.
  3. Configure your text editor:
    • If your editor supports LSP (VS Code, Neovim, Emacs, Kate, Helix, …), point it at dcd-server --lsp. See the installation guide for editor-specific instructions, and editors/code/ for the VS Code extension.
    • Otherwise, configure it to call the dcd-client program. See the wiki for information on configuring your specific editor.
  4. Start the dcd-server program before editing code. (Unless, of course, your editor’s plugin handles this for you)

Git + Make

  1. Install a recent D compiler.
  2. Run git submodule update --init --recursive after cloning this repository to grab the various dependencies.
  3. Optionally set the environment variable DC if you wish to use another compiler than the DMD known by the system.
  4. Run make to build the client and server. (Or run build.bat on Windows).

OS X w/ Homebrew

  1. brew install dcd

Dub

  1. dub build --build=release --config=client
  2. dub build --build=release --config=server

Sockets

TCP

On Windows DCD will use TCP sockets to communicate between the client and server. DCD can use TCP sockets on other operating systems if the client and server use the --tcp or --port command-line switches.

UNIX domain sockets

Operating systems that support UNIX domain sockets will use them by default. The path to the socket file can be overriden with the --socketFile option. These are the default paths:

OSX

The socket will be created at /var/tmp/dcd-${UID}.socket

Linux/BSD

The client and server will attempt to create the socket in the following locations:

  • ${XDG_RUNTIME_DIR}/dcd.socket
  • /tmp/dcd-${UID}.socket if XDG_RUNTIME_DIR is not defined.

Client

Because DCD is designed to be used from a text editor, this section is written primarily for plugin authors.

Get autocomplete information

The primary use case of the client is to query the server for autocomplete information. To do this, provide the client with the file that the user is editing along with the cursor position (in bytes).

dcd-client -c123 sourcefile.d

This will cause the client to print a listing of completions to stdout. The client will print either a listing of function call tips, or a listing of of completions depending on if the cursor was directly after a dot character or after a left parenthesis.

The file name is optional. If it is not specified, input will be read from stdin.

Dot completion

When the first line of output is “identifiers”, the editor should display a completion list.

Output format

A line containing the string “identifiers” followed by the completions that are available, one per line. Each line consists of the completion name followed by a tab character, followed by a completion kind

Completion kinds
  • c - class name
  • i - interface name
  • s - struct name
  • u - union name
  • v - variable name
  • m - member variable name
  • k - keyword, built-in version, scope statement
  • f - function or method
  • F - UFCS function
  • g - enum name
  • e - enum member
  • P - package name
  • M - module name
  • a - array
  • A - associative array
  • l - alias name
  • t - template name
  • T - mixin template name
  • h - template type parameter (when no colon constraint)
  • p - template variadic parameter
Example output
identifiers
parts	v
name	v
location	v
qualifier	v
kind	v
type	v
resolvedType	v
calltip	v
getPartByName	f
Extended output mode

You can pass --extended to dcd-client to get more information. Output will now be escaped (newlines get escaped to \n, tabs get escaped to \t, backslash gets escaped to \\).

Calltips are slightly different here because they first start with the function name instead of arguments and the second part will be blank. The actual calltip is now in the third column.

Columns may be empty, in which case there will be multiple tabs next to each other.

The following information will be available in every line for completion in this format then in a tab separated format:

  • identifier: raw name of a variable or function, etc
  • kind: empty for calltips, see above for rest
  • definition: function or variable definition string or close approximation for information display purpose
  • symbol location: in which file (or stdin) & byte offset this symbol is defined. Separated with a space.
  • documentation: escaped documentation string of this symbol
  • typeOf: resolved type name of this symbol:
    • For variables, fields, globals, constants: resolved type or empty if unresolved.
    • For functions: resolved return type or empty if unresolved.
    • For constructors: may be struct/class name or empty in any case.
    • Otherwise (probably) empty.
Example --extended output
identifiers
libraryFunction	f	Tuple!long libraryFunction(string s, string s2)	stdin 190	foobar
libraryFunction	f	int* libraryFunction(string s)	stdin 99	Hello\nWorld	int*
libraryVariable	v	int libraryVariable	stdin 56	My variable	int
libreTypes	g		stdin 298
Note

DCD’s output will start with “identifiers” when completing at a left paren character if the keywords pragma, scope, __traits, extern, or version were just before the paren.

Types in the calltips and typeOf column may not be complete, e.g. missing template parameters or typeof expressions, etc.

Parenthesis completion

When the first line of output is “calltips”, the editor should display a function call tip.

Output format

A line containing the string “calltips”, followed by zero or more lines, each containing a call tip for an overload of the given function.

Example output
calltips
Symbol findSymbolInCurrentScope(size_t cursorPosition, string name)

Doc comment display

dcd-client --doc -c 4298

When run with the --doc or -d option, DCD will attempt to display documentation comments associated with the symbol at the cursor position. In the case of functions there can be more than one documentation comment associated with a symbol. One doc comment will be printed per line. Newlines within the doc comments will be replaced with “\n”, and backslashes escaped as “\”.

Example output
An example doc comment\nParams: a = first param\n    Returns: nothing
An example doc comment\nParams: a = first param\n     b = second param\n    Returns: nothing

Clear server’s autocomplete cache

dcd-client --clearCache

Add import search path

Import paths can be added to the server without restarting it. To accomplish this, run the client with the -I option:

dcd-client -Ipath/to/imports

Remove import search path

Import paths can be removed from the server without restarting it. To accomplish this, run the client with the -R option:

dcd-client -Rpath/to/imports

Find declaration of symbol at cursor

dcd-client --symbolLocation -c 123

The “–symbolLocation” or “-l” flags cause the client to instruct the server to return the path to the file and the byte offset of the declaration of the symbol at the given cursor position.

The output consists of the absolute path to the file followed by a tab character followed by the byte offset, followed by a newline character. For example:

/home/example/src/project/bar.d	3482

Search for symbols by name

The “–search” or “-s” option causes the server to return location information for all symbols with the given name in both the file being edited as well as the server cache. The output format is one result per line, with the path, the symbol type, and the byte offset of the symbol separated by tab characters.

Example

Search the server’s cache for symbols named “toImpl”. (Using echo to give an EOF in place of a file being edited.)

echo | dcd-client --search toImpl

/usr/include/dmd/phobos/std/conv.d  f   48491
/usr/include/dmd/phobos/std/conv.d  f   47527
/usr/include/dmd/phobos/std/conv.d  f   47229
/usr/include/dmd/phobos/std/conv.d  f   40358
/usr/include/dmd/phobos/std/conv.d  f   38348
/usr/include/dmd/phobos/std/conv.d  f   35619
/usr/include/dmd/phobos/std/conv.d  f   32743
/usr/include/dmd/phobos/std/conv.d  f   22486
/usr/include/dmd/phobos/std/conv.d  f   16322
/usr/include/dmd/phobos/std/conv.d  f   14829
/usr/include/dmd/phobos/std/conv.d  f   14066
/usr/include/dmd/phobos/std/conv.d  f   13058
/usr/include/dmd/phobos/std/conv.d  f   12717
/usr/include/dmd/phobos/std/conv.d  f   9494

Find the use of the symbol at the cursor

dcd-client --localUse -c 123

The “–localUse” or “-u” flags cause the client to instruct the server to return all the uses, within the same module, of the symbol located at the given cursor position.

Output format

When uses exist, if the source symbol is an identifier (a type, a variable name, etc.) and if the symbol is not ambiguous then the first line contains the location of the symbol (a file name or literally stdin), a tab then the offset to the symbol declaration. Following the first line is a list of all known uses of the symbol in the current file. The list is composed of lines each containing a single number that indicates the byte offset from the start of the file to the i-th use.

Otherwise the client outputs 00000 so that the length of the answer is guaranteed to be at least 5 bytes.

Example output
stdin 45
26
45
133

Inlay Hints

Build a list of extra annoations for your IDE to display. You must submit the content of the current file displayed in your editor.

dcd-client --inlayHints

This is a W.I.P., currently it only provide annoatations about aliases for your variables, more is planned.

Example output
l   ->MyAlias->MyType   42

Server

The server must be running for the DCD client to provide autocomplete information. In future versions the client may start the server if it is not running, but for now it must be started manually or (usually) by an editor plugin.

Configuration Files

The server will attempt to read the file ${XDG_CONFIG_HOME}/dcd/dcd.conf (~/.config/dcd/dcd.conf if XDG_CONFIG_HOME is not set) on Posix systems, or dcd.conf on Windows in the current working directory on startup. If it exists, each line of the file is interpreted as a path that should be searched when looking for module imports. Lines that start with the “#” character are ignored. Lines can contain environment variables which will be expanded during loading. The name of the environment variable needs to the enclosed in ${VAR}. For example:

${HOME}/sysroot/usr/include/dmd/phobos

Lines starting with “!” add a module blacklist prefix: modules under that prefix are never read or indexed. The druntime core/internal namespace is always blacklisted; these lines only extend the list:

!myproject/internal

Keep in mind that DCD treats import paths the same way that the compiler does. For example, a configuration file like this will not work as expected:

/usr/include/dmd/

What you actually want is this:

/usr/include/dmd/druntime/import
/usr/include/dmd/phobos

Shut down the server

The server can be shut down by running the client with the --shutdown option:

dcd-client --shutdown

Import directories

Import directories can be specified on the command line at startup:

dcd-server -I/home/user/code/one -I/home/user/code/two

Port number

The --port or -p option lets you specify the port number that the server will listen on. The default port is 9166.

评论

暂无评论