├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── encoding.zig └── test ├── README.md ├── check.js ├── generate.js ├── package-lock.json ├── package.json └── scip ├── basic.bin ├── scip.proto └── scip.zig /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.zig text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 0 * * *' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, macos-latest, windows-latest] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | submodules: true 21 | - uses: actions/setup-node@v3 22 | with: 23 | node-version: 16.x 24 | - uses: goto-bus-stop/setup-zig@v1 25 | with: 26 | version: master 27 | 28 | - run: zig version 29 | - run: zig env 30 | 31 | - name: Run Tests 32 | run: | 33 | cd test 34 | npm ci 35 | 36 | node generate.js 37 | cd .. 38 | 39 | zig test encoding.zig 40 | cd test 41 | 42 | node check.js 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /zig-cache 2 | /zig-out 3 | /test/node_modules 4 | 5 | /test/scip/*.out 6 | /test/scip/*.json 7 | /test/scip/fuzzy.bin 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 protobruh contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # protobruh 2 | 3 | proto3 encoding/decoding for zig. Schema translation one day. Supports the bare minimum so that data based on `scip.proto` can properly be emitted for [scip-zig](https://github.com/zigtools/scip-zig). This is a tiny library so that I can easily copy-paste it into my project without adding any new dependencies. :) 4 | -------------------------------------------------------------------------------- /encoding.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | // COMMON 4 | 5 | pub const WireType = enum(usize) { 6 | varint_or_zigzag, 7 | fixed64bit, 8 | delimited, 9 | group_start, 10 | group_end, 11 | fixed32bit, 12 | }; 13 | 14 | pub const SplitTag = struct { field: usize, wire_type: WireType }; 15 | fn splitTag(tag: usize) SplitTag { 16 | return .{ .field = tag >> 3, .wire_type = @intToEnum(WireType, tag & 7) }; 17 | } 18 | 19 | fn joinTag(split: SplitTag) usize { 20 | return (split.field << 3) | @enumToInt(split.wire_type); 21 | } 22 | 23 | fn readTag(reader: anytype) !SplitTag { 24 | return splitTag(try std.leb.readULEB128(usize, reader)); 25 | } 26 | 27 | fn writeTag(writer: anytype, split: SplitTag) !void { 28 | try std.leb.writeULEB128(writer, joinTag(split)); 29 | } 30 | 31 | fn isArrayList(comptime T: type) bool { 32 | return @typeInfo(T) == .Struct and @hasField(T, "items") and @hasField(T, "capacity"); 33 | } 34 | 35 | // DECODE 36 | 37 | pub fn decode(comptime T: type, allocator: std.mem.Allocator, reader: anytype) !T { 38 | var value: T = undefined; 39 | try decodeInternal(T, &value, allocator, reader, true); 40 | return value; 41 | } 42 | 43 | fn decodeMessageFields(comptime T: type, allocator: std.mem.Allocator, reader: anytype, length: usize) !T { 44 | var counting_reader = std.io.countingReader(reader); 45 | var value = if (@hasField(T, "items") and @hasField(T, "capacity")) .{} else std.mem.zeroInit(T, .{}); 46 | 47 | while (length == 0 or counting_reader.bytes_read < length) { 48 | // TODO: Add type sameness checks 49 | const split = readTag(counting_reader.reader()) catch |err| switch (err) { 50 | error.EndOfStream => return value, 51 | else => return err, 52 | }; 53 | 54 | inline for (@field(T, "tags")) |rel| { 55 | if (split.field == rel[1]) { 56 | decodeInternal(@TypeOf(@field(value, rel[0])), &@field(value, rel[0]), allocator, counting_reader.reader(), false) catch |err| switch (err) { 57 | error.EndOfStream => return value, 58 | else => return err, 59 | }; 60 | } 61 | } 62 | } 63 | 64 | return value; 65 | } 66 | 67 | fn decodeInternal( 68 | comptime T: type, 69 | value: *T, 70 | allocator: std.mem.Allocator, 71 | reader: anytype, 72 | top: bool, 73 | ) !void { 74 | switch (@typeInfo(T)) { 75 | .Struct => { 76 | if (comptime isArrayList(T)) { 77 | const Child = @typeInfo(@field(T, "Slice")).Pointer.child; 78 | const cti = @typeInfo(Child); 79 | 80 | if (cti == .Int or cti == .Enum) { 81 | var lim = std.io.limitedReader(reader, try std.leb.readULEB128(usize, reader)); 82 | while (true) 83 | try value.append(allocator, decode(Child, allocator, lim.reader()) catch return); 84 | } else { 85 | var new_elem: Child = undefined; 86 | try decodeInternal(Child, &new_elem, allocator, reader, false); 87 | try value.append(allocator, new_elem); 88 | } 89 | } else { 90 | var length = if (top) 0 else try std.leb.readULEB128(usize, reader); 91 | value.* = try decodeMessageFields(T, allocator, reader, length); 92 | } 93 | }, 94 | .Pointer => |ptr| { 95 | _ = ptr; 96 | // TODO: Handle non-slices 97 | if (T == []const u8) { 98 | var data = try allocator.alloc(u8, try std.leb.readULEB128(usize, reader)); 99 | _ = try reader.readAll(data); 100 | value.* = data; 101 | } else @compileError("Slices not implemented"); 102 | }, 103 | // TODO: non-usize enums 104 | .Enum => value.* = @intToEnum(T, try std.leb.readULEB128(usize, reader)), 105 | .Int => |i| value.* = switch (i.signedness) { 106 | .signed => try std.leb.readILEB128(T, reader), 107 | .unsigned => try std.leb.readULEB128(T, reader), 108 | }, 109 | .Bool => value.* = ((try std.leb.readULEB128(usize, reader)) != 0), 110 | .Array => |arr| { 111 | const Child = arr.child; 112 | const cti = @typeInfo(Child); 113 | 114 | if (cti == .Int or cti == .Enum) { 115 | var lim = std.io.limitedReader(reader, try std.leb.readULEB128(usize, reader)); 116 | var array: [arr.len]Child = undefined; 117 | var index: usize = 0; 118 | while (true) : (index += 1) { 119 | const new_item = decode(Child, allocator, lim.reader()) catch break; 120 | if (index == array.len) return error.IndexOutOfRange; 121 | array[index] = new_item; 122 | } 123 | if (index != array.len) return error.ArrayNotFilled; 124 | 125 | value.* = array; 126 | } else { 127 | @compileError("Array not of ints/enums not supported for decoding!"); 128 | } 129 | }, 130 | else => @compileError("Unsupported: " ++ @typeName(T)), 131 | } 132 | } 133 | 134 | test "Decode basic" { 135 | const scip = @import("test/scip/scip.zig"); 136 | 137 | var file = try std.fs.cwd().openFile("test/scip/basic.bin", .{}); 138 | defer file.close(); 139 | 140 | var reader = file.reader(); 141 | 142 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 143 | defer arena.deinit(); 144 | 145 | const index = try decode(scip.Index, arena.allocator(), reader); 146 | try std.testing.expectEqual(scip.ProtocolVersion.unspecified_protocol_version, index.metadata.version); 147 | try std.testing.expectEqualStrings("joe", index.metadata.tool_info.name); 148 | try std.testing.expectEqualStrings("mama", index.metadata.tool_info.version); 149 | try std.testing.expectEqual(@as(usize, 2), index.metadata.tool_info.arguments.items.len); 150 | try std.testing.expectEqualStrings("amog", index.metadata.tool_info.arguments.items[0]); 151 | try std.testing.expectEqualStrings("us", index.metadata.tool_info.arguments.items[1]); 152 | try std.testing.expectEqualStrings("C:\\Programming\\Zig\\scip-zig\\test", index.metadata.project_root); 153 | try std.testing.expect(index.metadata.text_document_encoding == .utf8); 154 | try std.testing.expectEqual(@as(usize, 1), index.documents.items.len); 155 | try std.testing.expectEqualStrings("zig", index.documents.items[0].language); 156 | try std.testing.expectEqualStrings("loris.zig", index.documents.items[0].relative_path); 157 | try std.testing.expectEqual(@as(usize, 0), index.documents.items[0].occurrences.items.len); 158 | 159 | // TODO: Check more of this result 160 | 161 | // try std.testing.expectEqual(scip.Index{ 162 | // .metadata = .{ 163 | // .version = .unspecified_protocol_version, 164 | // .tool_info = .{ 165 | // .name = "joe", 166 | // .version = "mama", 167 | // .arguments = try TestUtils.unmanagedFromSlice([]const u8, arena.allocator(), &.{ "amog", "us" }), 168 | // }, 169 | // .project_root = "C:\\Programming\\Zig\\scip-zig\\test", 170 | // .text_document_encoding = .utf8, 171 | // }, 172 | // .documents = try TestUtils.unmanagedFromSlice(scip.Document, arena.allocator(), &.{ 173 | // .{ 174 | // .language = "zig", 175 | // .relative_path = "loris.zig", 176 | // .occurrences = .{}, 177 | // .symbols = try TestUtils.unmanagedFromSlice(scip.SymbolInformation, arena.allocator(), &.{ 178 | // .{ 179 | // .symbol = "swag", 180 | // .documentation = try TestUtils.unmanagedFromSlice([]const u8, arena.allocator(), &.{ "Is Loris swag?", "Yes" }), 181 | // .relationships = .{}, 182 | // }, 183 | // }), 184 | // }, 185 | // }), 186 | // .external_symbols = .{}, 187 | // }, index); 188 | } 189 | 190 | // ENCODE 191 | 192 | pub fn encode(value: anytype, writer: anytype) !void { 193 | try encodeInternal(value, writer, true); 194 | } 195 | 196 | fn typeToWireType(comptime T: type) WireType { 197 | if (@typeInfo(T) == .Struct or @typeInfo(T) == .Pointer or @typeInfo(T) == .Array) return .delimited; 198 | if (@typeInfo(T) == .Int or @typeInfo(T) == .Bool or @typeInfo(T) == .Enum) return .varint_or_zigzag; 199 | @compileError("Wire type not handled: " ++ @typeName(T)); 200 | } 201 | 202 | fn encodeMessageFields(value: anytype, writer: anytype) !void { 203 | const T = @TypeOf(value); 204 | inline for (@field(T, "tags")) |rel| { 205 | const subval = @field(value, rel[0]); 206 | const SubT = @TypeOf(subval); 207 | 208 | if (comptime isArrayList(SubT) and !b: { 209 | const Child = @typeInfo(@field(SubT, "Slice")).Pointer.child; 210 | const cti = @typeInfo(Child); 211 | break :b cti == .Int or cti == .Enum; 212 | }) { 213 | for (subval.items) |item| { 214 | try writeTag(writer, .{ .field = rel[1], .wire_type = typeToWireType(@TypeOf(item)) }); 215 | try encodeInternal(item, writer, false); 216 | } 217 | } else { 218 | try writeTag(writer, .{ .field = rel[1], .wire_type = typeToWireType(SubT) }); 219 | try encodeInternal(subval, writer, false); 220 | } 221 | } 222 | } 223 | 224 | fn encodeInternal( 225 | value: anytype, 226 | writer: anytype, 227 | top: bool, 228 | ) !void { 229 | const T = @TypeOf(value); 230 | switch (@typeInfo(T)) { 231 | .Struct => { 232 | if (comptime isArrayList(T)) { 233 | var count_writer = std.io.countingWriter(std.io.null_writer); 234 | for (value.items) |item| try encodeInternal(item, count_writer.writer(), false); 235 | try std.leb.writeULEB128(writer, count_writer.bytes_written); 236 | for (value.items) |item| try encodeInternal(item, writer, false); 237 | } else { 238 | if (!top) { 239 | var count_writer = std.io.countingWriter(std.io.null_writer); 240 | try encodeMessageFields(value, count_writer.writer()); 241 | try std.leb.writeULEB128(writer, count_writer.bytes_written); 242 | } 243 | try encodeMessageFields(value, writer); 244 | } 245 | }, 246 | .Pointer => |ptr| { 247 | _ = ptr; 248 | // TODO: Handle non-slices 249 | if (T == []const u8) { 250 | try std.leb.writeULEB128(writer, value.len); 251 | try writer.writeAll(value); 252 | } else @compileError("Slices not implemented"); 253 | }, 254 | // TODO: non-usize enums 255 | .Enum => try std.leb.writeULEB128(writer, @enumToInt(value)), 256 | .Int => |i| switch (i.signedness) { 257 | .signed => try std.leb.writeILEB128(writer, value), 258 | .unsigned => try std.leb.writeULEB128(writer, value), 259 | }, 260 | .Bool => try std.leb.writeULEB128(writer, @boolToInt(value)), 261 | .Array => { 262 | var count_writer = std.io.countingWriter(std.io.null_writer); 263 | for (value) |item| try encodeInternal(item, count_writer.writer(), false); 264 | try std.leb.writeULEB128(writer, count_writer.bytes_written); 265 | for (value) |item| try encodeInternal(item, writer, false); 266 | }, 267 | else => @compileError("Unsupported: " ++ @typeName(T)), 268 | } 269 | } 270 | 271 | test "Decode and re-encode fuzzy" { 272 | const scip = @import("test/scip/scip.zig"); 273 | 274 | var file = try std.fs.cwd().openFile("test/scip/fuzzy.bin", .{}); 275 | defer file.close(); 276 | 277 | var reader = file.reader(); 278 | 279 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 280 | defer arena.deinit(); 281 | 282 | const index = try decode(scip.Index, arena.allocator(), reader); 283 | 284 | var out_file = try std.fs.cwd().createFile("test/scip/fuzzy.bin.out", .{}); 285 | defer out_file.close(); 286 | 287 | try encode(index, out_file.writer()); 288 | } 289 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # protobruh/test 2 | 3 | We use an existing protobuf implementation ([protobuf.js](https://github.com/protobufjs/protobuf.js)) to encode and decode our test files and ensure that ours is up to spec. 4 | 5 | - `generate.js`: Generate basic.bin and fuzzy.bin 6 | - `check.js`: Check if fuzzy.bin.out and fuzzy.json match 7 | -------------------------------------------------------------------------------- /test/check.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const diff = require("diff"); 4 | const protobuf = require("protobufjs"); 5 | 6 | (async () => { 7 | const root = await protobuf.load(path.join(__dirname, "scip/scip.proto")); 8 | 9 | const Index = root.lookupType("scip.Index"); 10 | const actual = Index.decode(fs.readFileSync(path.join(__dirname, "scip/fuzzy.bin.out"))).toJSON(); 11 | const expected = JSON.parse(fs.readFileSync(path.join(__dirname, "scip/fuzzy.json")).toString()); 12 | 13 | const isCorrect = JSON.stringify(actual) === JSON.stringify(expected); 14 | console.log("isCorrect: " + isCorrect) 15 | if (!isCorrect) { 16 | const d = diff.diffJson(expected, actual); 17 | for (const part of d) { 18 | const color = part.added ? '+' : 19 | part.removed ? '-' : '*'; 20 | for (const line of part.value.split("\n")) { 21 | console.error(`${color} ${line}`); 22 | } 23 | // console.log(`${part.value}`); 24 | } 25 | } 26 | process.exit(isCorrect ? 0 : 1); 27 | })(); 28 | -------------------------------------------------------------------------------- /test/generate.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const protobuf = require("protobufjs"); 4 | 5 | /** 6 | * @param {protobuf.Root} root 7 | * @param {protobuf.Enum} en 8 | * @returns {number} 9 | */ 10 | function fuzzEnum(root, en) { 11 | const values = Object.values(en.values); 12 | return values[Math.floor(Math.random() * values.length)]; 13 | } 14 | 15 | /** 16 | * 17 | * @param {protobuf.Root} root 18 | * @param {protobuf.Type} type 19 | * @returns {protobuf.Message} 20 | */ 21 | function fuzzType(root, type) { 22 | var obj = {}; 23 | 24 | // console.log(type.name); 25 | 26 | for (const [name, field] of Object.entries(type.fields)) { 27 | const res = root.lookup(field.type); 28 | let values = []; 29 | 30 | const e = name === "range" ? 4 : (field.repeated ? Math.ceil(Math.random() * 12) : 1); 31 | for (let i = 0; i < e; i++) { 32 | if (res) { 33 | values.push(res.values ? fuzzEnum(root, res) : fuzzType(root, res)); 34 | } else { 35 | // console.log(`${name}: "${field.type}"`); 36 | if (field.type === "string") values.push(Math.random().toString(36).slice(2)); 37 | else if (field.type === "int32") values.push(Math.floor(Math.random() * 100)); 38 | else if (field.type === "bool") values.push(Math.random() > 0.5); 39 | else throw new Error("haze: " + field.type); 40 | } 41 | } 42 | 43 | obj[name] = field.repeated ? values : values[0]; 44 | } 45 | 46 | // console.log(obj); 47 | 48 | const err = type.verify(obj); 49 | if (err) throw new Error(err); 50 | return type.create(obj); 51 | } 52 | 53 | (async () => { 54 | const root = await protobuf.load(path.join(__dirname, "scip/scip.proto")); 55 | 56 | const Index = root.lookupType("scip.Index"); 57 | const Metadata = root.lookupType("scip.Metadata"); 58 | const Document = root.lookupType("scip.Document"); 59 | const ProtocolVersion = root.lookupEnum("scip.ProtocolVersion"); 60 | const TextEncoding = root.lookupEnum("scip.TextEncoding"); 61 | const SymbolInformation = root.lookupType("scip.SymbolInformation"); 62 | 63 | // basic.bin 64 | const payload = { 65 | metadata: Metadata.create({ 66 | version: ProtocolVersion.values.UnspecifiedProtocolVersion, 67 | toolInfo: { 68 | name: "joe", 69 | version: "mama", 70 | arguments: [ "amog", "us" ], 71 | }, 72 | projectRoot: "C:\\Programming\\Zig\\scip-zig\\test", 73 | textDocumentEncoding: TextEncoding.values.UTF8, 74 | }), 75 | documents: [ 76 | Document.create({ 77 | language: "zig", 78 | relativePath: "loris.zig", 79 | occurrences: [], 80 | symbols: [ 81 | SymbolInformation.create({ 82 | symbol: "swag", 83 | documentation: ["Is Loris swag?", "Yes"], 84 | relationships: [], 85 | }), 86 | ], 87 | }), 88 | ], 89 | externalSymbols: [] 90 | } 91 | 92 | const errMsg = Index.verify(payload); 93 | if (errMsg) 94 | throw Error(errMsg); 95 | 96 | const message = Index.create(payload); 97 | const buffer = Index.encode(message).finish(); 98 | fs.writeFileSync(path.join(__dirname, "scip/basic.bin"), buffer); 99 | 100 | // console.log(root.lookupType("scip.Diagnostica")); 101 | const ft = fuzzType(root, Index); 102 | fs.writeFileSync(path.join(__dirname, "scip/fuzzy.bin"), Index.encode(ft).finish()); 103 | fs.writeFileSync(path.join(__dirname, "scip/fuzzy.json"), JSON.stringify(ft.toJSON(), null, 4)); 104 | })(); 105 | -------------------------------------------------------------------------------- /test/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "diff": "^5.1.0", 9 | "protobufjs": "^7.1.1" 10 | } 11 | }, 12 | "node_modules/@protobufjs/aspromise": { 13 | "version": "1.1.2", 14 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 15 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 16 | }, 17 | "node_modules/@protobufjs/base64": { 18 | "version": "1.1.2", 19 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 20 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 21 | }, 22 | "node_modules/@protobufjs/codegen": { 23 | "version": "2.0.4", 24 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 25 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 26 | }, 27 | "node_modules/@protobufjs/eventemitter": { 28 | "version": "1.1.0", 29 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 30 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 31 | }, 32 | "node_modules/@protobufjs/fetch": { 33 | "version": "1.1.0", 34 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 35 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 36 | "dependencies": { 37 | "@protobufjs/aspromise": "^1.1.1", 38 | "@protobufjs/inquire": "^1.1.0" 39 | } 40 | }, 41 | "node_modules/@protobufjs/float": { 42 | "version": "1.0.2", 43 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 44 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 45 | }, 46 | "node_modules/@protobufjs/inquire": { 47 | "version": "1.1.0", 48 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 49 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 50 | }, 51 | "node_modules/@protobufjs/path": { 52 | "version": "1.1.2", 53 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 54 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 55 | }, 56 | "node_modules/@protobufjs/pool": { 57 | "version": "1.1.0", 58 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 59 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 60 | }, 61 | "node_modules/@protobufjs/utf8": { 62 | "version": "1.1.0", 63 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 64 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 65 | }, 66 | "node_modules/@types/node": { 67 | "version": "18.7.18", 68 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", 69 | "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" 70 | }, 71 | "node_modules/diff": { 72 | "version": "5.1.0", 73 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", 74 | "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", 75 | "engines": { 76 | "node": ">=0.3.1" 77 | } 78 | }, 79 | "node_modules/long": { 80 | "version": "5.2.0", 81 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", 82 | "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" 83 | }, 84 | "node_modules/protobufjs": { 85 | "version": "7.1.1", 86 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.1.tgz", 87 | "integrity": "sha512-d0nMQqS/aT3lfV8bKi9Gbg73vPd2LcDdTDOu6RE/M+h9DY8g1EmDzk3ADPccthEWfTBjkR2oxNdx9Gs8YubT+g==", 88 | "hasInstallScript": true, 89 | "dependencies": { 90 | "@protobufjs/aspromise": "^1.1.2", 91 | "@protobufjs/base64": "^1.1.2", 92 | "@protobufjs/codegen": "^2.0.4", 93 | "@protobufjs/eventemitter": "^1.1.0", 94 | "@protobufjs/fetch": "^1.1.0", 95 | "@protobufjs/float": "^1.0.2", 96 | "@protobufjs/inquire": "^1.1.0", 97 | "@protobufjs/path": "^1.1.2", 98 | "@protobufjs/pool": "^1.1.0", 99 | "@protobufjs/utf8": "^1.1.0", 100 | "@types/node": ">=13.7.0", 101 | "long": "^5.0.0" 102 | }, 103 | "engines": { 104 | "node": ">=12.0.0" 105 | } 106 | } 107 | }, 108 | "dependencies": { 109 | "@protobufjs/aspromise": { 110 | "version": "1.1.2", 111 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 112 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 113 | }, 114 | "@protobufjs/base64": { 115 | "version": "1.1.2", 116 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 117 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 118 | }, 119 | "@protobufjs/codegen": { 120 | "version": "2.0.4", 121 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 122 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 123 | }, 124 | "@protobufjs/eventemitter": { 125 | "version": "1.1.0", 126 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 127 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 128 | }, 129 | "@protobufjs/fetch": { 130 | "version": "1.1.0", 131 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 132 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 133 | "requires": { 134 | "@protobufjs/aspromise": "^1.1.1", 135 | "@protobufjs/inquire": "^1.1.0" 136 | } 137 | }, 138 | "@protobufjs/float": { 139 | "version": "1.0.2", 140 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 141 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 142 | }, 143 | "@protobufjs/inquire": { 144 | "version": "1.1.0", 145 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 146 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 147 | }, 148 | "@protobufjs/path": { 149 | "version": "1.1.2", 150 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 151 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 152 | }, 153 | "@protobufjs/pool": { 154 | "version": "1.1.0", 155 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 156 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 157 | }, 158 | "@protobufjs/utf8": { 159 | "version": "1.1.0", 160 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 161 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 162 | }, 163 | "@types/node": { 164 | "version": "18.7.18", 165 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", 166 | "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" 167 | }, 168 | "diff": { 169 | "version": "5.1.0", 170 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", 171 | "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" 172 | }, 173 | "long": { 174 | "version": "5.2.0", 175 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", 176 | "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" 177 | }, 178 | "protobufjs": { 179 | "version": "7.1.1", 180 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.1.tgz", 181 | "integrity": "sha512-d0nMQqS/aT3lfV8bKi9Gbg73vPd2LcDdTDOu6RE/M+h9DY8g1EmDzk3ADPccthEWfTBjkR2oxNdx9Gs8YubT+g==", 182 | "requires": { 183 | "@protobufjs/aspromise": "^1.1.2", 184 | "@protobufjs/base64": "^1.1.2", 185 | "@protobufjs/codegen": "^2.0.4", 186 | "@protobufjs/eventemitter": "^1.1.0", 187 | "@protobufjs/fetch": "^1.1.0", 188 | "@protobufjs/float": "^1.0.2", 189 | "@protobufjs/inquire": "^1.1.0", 190 | "@protobufjs/path": "^1.1.2", 191 | "@protobufjs/pool": "^1.1.0", 192 | "@protobufjs/utf8": "^1.1.0", 193 | "@types/node": ">=13.7.0", 194 | "long": "^5.0.0" 195 | } 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "diff": "^5.1.0", 4 | "protobufjs": "^7.1.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/scip/basic.bin: -------------------------------------------------------------------------------- 1 | 2 | = 3 | joemamaamogus C:\Programming\Zig\scip-zig\test - 4 | loris.zig 5 | swagIs Loris swag?Yes"zig -------------------------------------------------------------------------------- /test/scip/scip.proto: -------------------------------------------------------------------------------- 1 | // See license at https://github.com/sourcegraph/scip/blob/main/LICENSE 2 | // An index contains one or more pieces of information about a given piece of 3 | // source code or software artifact. Complementary information can be merged 4 | // together from multiple sources to provide a unified code intelligence 5 | // experience. 6 | // 7 | // Programs producing a file of this format is an "indexer" and may operate 8 | // somewhere on the spectrum between precision, such as indexes produced by 9 | // compiler-backed indexers, and heurstics, such as indexes produced by local 10 | // syntax-directed analysis for scope rules. 11 | 12 | syntax = "proto3"; 13 | 14 | package scip; 15 | 16 | option go_package = "github.com/sourcegraph/scip/bindings/go/scip/"; 17 | 18 | // Index represents a complete SCIP index for a workspace this is rooted at a 19 | // single directory. An Index message payload can have a large memory footprint 20 | // and it's therefore recommended to emit and consume an Index payload one field 21 | // value at a time. To permit streaming consumption of an Index payload, the 22 | // `metadata` field must appear at the start of the stream and must only appear 23 | // once in the stream. Other field values may appear in any order. 24 | message Index { 25 | // Metadata about this index. 26 | Metadata metadata = 1; 27 | // Documents that belong to this index. 28 | repeated Document documents = 2; 29 | // (optional) Symbols that are referenced from this index but are defined in 30 | // an external package (a separate `Index` message). Leave this field empty 31 | // if you assume the external package will get indexed separately. If the 32 | // external package won't get indexed for some reason then you can use this 33 | // field to provide hover documentation for those external symbols. 34 | repeated SymbolInformation external_symbols = 3; 35 | } 36 | 37 | message Metadata { 38 | // Which version of this protocol was used to generate this index? 39 | ProtocolVersion version = 1; 40 | // Information about the tool that produced this index. 41 | ToolInfo tool_info = 2; 42 | // URI-encoded absolute path to the root directory of this index. All 43 | // documents in this index must appear in a subdirectory of this root 44 | // directory. 45 | string project_root = 3; 46 | // Text encoding of the source files on disk that are referenced from 47 | // `Document.relative_path`. 48 | TextEncoding text_document_encoding = 4; 49 | } 50 | 51 | enum ProtocolVersion { 52 | UnspecifiedProtocolVersion = 0; 53 | } 54 | 55 | enum TextEncoding { 56 | UnspecifiedTextEncoding = 0; 57 | UTF8 = 1; 58 | UTF16 = 2; 59 | } 60 | 61 | message ToolInfo { 62 | // Name of the indexer that produced this index. 63 | string name = 1; 64 | // Version of the indexer that produced this index. 65 | string version = 2; 66 | // Command-line arguments that were used to invoke this indexer. 67 | repeated string arguments = 3; 68 | } 69 | 70 | // Document defines the metadata about a source file on disk. 71 | message Document { 72 | // The string ID for the programming language this file is written in. 73 | // The `Language` enum contains the names of most common programming languages. 74 | // This field is typed as a string to permit any programming langauge, including 75 | // ones that are not specified by the `Language` enum. 76 | string language = 4; 77 | // (Required) Unique path to the text document. 78 | // 79 | // 1. The path must be relative to the directory supplied in the associated 80 | // `Metadata.project_root`. 81 | // 2. The path must not begin with a leading '/'. 82 | // 3. The path must point to a regular file, not a symbolic link. 83 | // 4. The path must use '/' as the separator, including on Windows. 84 | // 5. The path must be canonical; it cannot include empty components ('//'), 85 | // or '.' or '..'. 86 | string relative_path = 1; 87 | // Occurrences that appear in this file. 88 | repeated Occurrence occurrences = 2; 89 | // Symbols that are defined within this document. 90 | repeated SymbolInformation symbols = 3; 91 | } 92 | 93 | 94 | // Symbol is similar to a URI, it identifies a class, method, or a local 95 | // variable. `SymbolInformation` contains rich metadata about symbols such as 96 | // the docstring. 97 | // 98 | // Symbol has a standardized string representation, which can be used 99 | // interchangeably with `Symbol`. The syntax for Symbol is the following: 100 | // ``` 101 | // # ()+ stands for one or more repetitions of 102 | // ::= ' ' ' ' ()+ | 'local ' 103 | // ::= ' ' ' ' 104 | // ::= any UTF-8, escape spaces with double space. 105 | // ::= same as above, use the placeholder '.' to indicate an empty value 106 | // ::= same as above 107 | // ::= same as above 108 | // ::= | | | | | | 109 | // ::= '/' 110 | // ::= '#' 111 | // ::= '.' 112 | // ::= ':' 113 | // ::= '(' ').' 114 | // ::= '[' ']' 115 | // ::= '(' ')' 116 | // ::= 117 | // ::= 118 | // ::= | 119 | // ::= ()+ 120 | // ::= '_' | '+' | '-' | '$' | ASCII letter or digit 121 | // ::= '`' ()+ '`' 122 | // ::= any UTF-8 character, escape backticks with double backtick. 123 | // ``` 124 | // 125 | // The list of descriptors for a symbol should together form a fully 126 | // qualified name for the symbol. That is, it should serve as a unique 127 | // identifier across the package. Typically, it will include one descriptor 128 | // for every node in the AST (along the ancestry path) between the root of 129 | // the file and the node corresponding to the symbol. 130 | message Symbol { 131 | string scheme = 1; 132 | Package package = 2; 133 | repeated Descriptor descriptors = 3; 134 | } 135 | 136 | // Unit of packaging and distribution. 137 | // 138 | // NOTE: This corresponds to a module in Go and JVM languages. 139 | message Package { 140 | string manager = 1; 141 | string name = 2; 142 | string version = 3; 143 | } 144 | 145 | message Descriptor { 146 | enum Suffix { 147 | option allow_alias = true; 148 | UnspecifiedSuffix = 0; 149 | // Unit of code abstraction and/or namespacing. 150 | // 151 | // NOTE: This corresponds to a package in Go and JVM languages. 152 | Namespace = 1; 153 | // Use Namespace instead. 154 | Package = 1 [deprecated=true]; 155 | Type = 2; 156 | Term = 3; 157 | Method = 4; 158 | TypeParameter = 5; 159 | Parameter = 6; 160 | Macro = 9; 161 | // Can be used for any purpose. 162 | Meta = 7; 163 | Local = 8; 164 | } 165 | string name = 1; 166 | string disambiguator = 2; 167 | Suffix suffix = 3; 168 | } 169 | 170 | // SymbolInformation defines metadata about a symbol, such as the symbol's 171 | // docstring or what package it's defined it. 172 | message SymbolInformation { 173 | // Identifier of this symbol, which can be referenced from `Occurence.symbol`. 174 | // The string must be formatted according to the grammar in `Symbol`. 175 | string symbol = 1; 176 | // (optional, but strongly recommended) The markdown-formatted documentation 177 | // for this symbol. This field is repeated to allow different kinds of 178 | // documentation. For example, it's nice to include both the signature of a 179 | // method (parameters and return type) along with the accompanying docstring. 180 | repeated string documentation = 3; 181 | // (optional) Relationships to other symbols (e.g., implements, type definition). 182 | repeated Relationship relationships = 4; 183 | } 184 | 185 | message Relationship { 186 | string symbol = 1; 187 | // When resolving "Find references", this field documents what other symbols 188 | // should be included together with this symbol. For example, consider the 189 | // following TypeScript code that defines two symbols `Animal#sound()` and 190 | // `Dog#sound()`: 191 | // ```ts 192 | // interface Animal { 193 | // ^^^^^^ definition Animal# 194 | // sound(): string 195 | // ^^^^^ definition Animal#sound() 196 | // } 197 | // class Dog implements Animal { 198 | // ^^^ definition Dog#, implementation_symbols = Animal# 199 | // public sound(): string { return "woof" } 200 | // ^^^^^ definition Dog#sound(), references_symbols = Animal#sound(), implementation_symbols = Animal#sound() 201 | // } 202 | // const animal: Animal = new Dog() 203 | // ^^^^^^ reference Animal# 204 | // console.log(animal.sound()) 205 | // ^^^^^ reference Animal#sound() 206 | // ``` 207 | // Doing "Find references" on the symbol `Animal#sound()` should return 208 | // references to the `Dog#sound()` method as well. Vice-versa, doing "Find 209 | // references" on the `Dog#sound()` method should include references to the 210 | // `Animal#sound()` method as well. 211 | bool is_reference = 2; 212 | // Similar to `references_symbols` but for "Go to implementation". 213 | // It's common for the `implementation_symbols` and `references_symbols` fields 214 | // have the same values but that's not always the case. 215 | // In the TypeScript example above, observe that `implementation_symbols` has 216 | // the value `"Animal#"` for the "Dog#" symbol while `references_symbols` is 217 | // empty. When requesting "Find references" on the "Animal#" symbol we don't 218 | // want to include references to "Dog#" even if "Go to implementation" on the 219 | // "Animal#" symbol should navigate to the "Dog#" symbol. 220 | bool is_implementation = 3; 221 | // Similar to `references_symbols` but for "Go to type definition". 222 | bool is_type_definition = 4; 223 | } 224 | 225 | // SymbolRole declares what "role" a symbol has in an occurrence. A role is 226 | // encoded as a bitset where each bit represents a different role. For example, 227 | // to determine if the `Import` role is set, test whether the second bit of the 228 | // enum value is defined. In pseudocode, this can be implemented with the 229 | // logic: `const isImportRole = (role.value & SymbolRole.Import.value) > 0`. 230 | enum SymbolRole { 231 | // This case is not meant to be used; it only exists to avoid an error 232 | // from the Protobuf code generator. 233 | UnspecifiedSymbolRole = 0; 234 | // Is the symbol defined here? If not, then this is a symbol reference. 235 | Definition = 0x1; 236 | // Is the symbol imported here? 237 | Import = 0x2; 238 | // Is the symbol written here? 239 | WriteAccess = 0x4; 240 | // Is the symbol read here? 241 | ReadAccess = 0x8; 242 | // Is the symbol in generated code? 243 | Generated = 0x10; 244 | // Is the symbol in test code? 245 | Test = 0x20; 246 | } 247 | 248 | enum SyntaxKind { 249 | option allow_alias = true; 250 | 251 | UnspecifiedSyntaxKind = 0; 252 | 253 | // Comment, including comment markers and text 254 | Comment = 1; 255 | 256 | // `;` `.` `,` 257 | PunctuationDelimiter = 2; 258 | // (), {}, [] when used syntactically 259 | PunctuationBracket = 3; 260 | 261 | // `if`, `else`, `return`, `class`, etc. 262 | Keyword = 4; 263 | IdentifierKeyword = 4 [deprecated=true]; 264 | 265 | // `+`, `*`, etc. 266 | IdentifierOperator = 5; 267 | 268 | // non-specific catch-all for any identifier not better described elsewhere 269 | Identifier = 6; 270 | // Identifiers builtin to the language: `min`, `print` in Python. 271 | IdentifierBuiltin = 7; 272 | // Identifiers representing `null`-like values: `None` in Python, `nil` in Go. 273 | IdentifierNull = 8; 274 | // `xyz` in `const xyz = "hello"` 275 | IdentifierConstant = 9; 276 | // `var X = "hello"` in Go 277 | IdentifierMutableGlobal = 10; 278 | // Parameter definition and references 279 | IdentifierParameter = 11; 280 | // Identifiers for variable definitions and references within a local scope 281 | IdentifierLocal = 12; 282 | // Identifiers that shadow other identifiers in an outer scope 283 | IdentifierShadowed = 13; 284 | // Identifier representing a unit of code abstraction and/or namespacing. 285 | // 286 | // NOTE: This corresponds to a package in Go and JVM languages, 287 | // and a module in languages like Python and JavaScript. 288 | IdentifierNamespace = 14; 289 | IdentifierModule = 14 [deprecated=true]; 290 | 291 | // Function references, including calls 292 | IdentifierFunction = 15; 293 | // Function definition only 294 | IdentifierFunctionDefinition = 16; 295 | 296 | // Macro references, including invocations 297 | IdentifierMacro = 17; 298 | // Macro definition only 299 | IdentifierMacroDefinition = 18; 300 | 301 | // non-builtin types 302 | IdentifierType = 19; 303 | // builtin types only, such as `str` for Python or `int` in Go 304 | IdentifierBuiltinType = 20; 305 | 306 | // Python decorators, c-like __attribute__ 307 | IdentifierAttribute = 21; 308 | 309 | // `\b` 310 | RegexEscape = 22; 311 | // `*`, `+` 312 | RegexRepeated = 23; 313 | // `.` 314 | RegexWildcard = 24; 315 | // `(`, `)`, `[`, `]` 316 | RegexDelimiter = 25; 317 | // `|`, `-` 318 | RegexJoin = 26; 319 | 320 | // Literal strings: "Hello, world!" 321 | StringLiteral = 27; 322 | // non-regex escapes: "\t", "\n" 323 | StringLiteralEscape = 28; 324 | // datetimes within strings, special words within a string, `{}` in format strings 325 | StringLiteralSpecial = 29; 326 | // "key" in { "key": "value" }, useful for example in JSON 327 | StringLiteralKey = 30; 328 | // 'c' or similar, in languages that differentiate strings and characters 329 | CharacterLiteral = 31; 330 | // Literal numbers, both floats and integers 331 | NumericLiteral = 32; 332 | // `true`, `false` 333 | BooleanLiteral = 33; 334 | 335 | // Used for XML-like tags 336 | Tag = 34; 337 | // Attribute name in XML-like tags 338 | TagAttribute = 35; 339 | // Delimiters for XML-like tags 340 | TagDelimiter = 36; 341 | } 342 | 343 | // Occurrence associates a source position with a symbol and/or highlighting 344 | // information. 345 | // 346 | // If possible, indexers should try to bundle logically related information 347 | // across occurrences into a single occurrence to reduce payload sizes. 348 | message Occurrence { 349 | // Source position of this occurrence. Must be exactly three or four 350 | // elements: 351 | // 352 | // - Four elements: `[startLine, startCharacter, endLine, endCharacter]` 353 | // - Three elements: `[startLine, startCharacter, endCharacter]`. The end line 354 | // is inferred to have the same value as the start line. 355 | // 356 | // Line numbers and characters are always 0-based. Make sure to increment the 357 | // line/character values before displaying them in an editor-like UI because 358 | // editors conventionally use 1-based numbers. 359 | // 360 | // Historical note: the original draft of this schema had a `Range` message 361 | // type with `start` and `end` fields of type `Position`, mirroring LSP. 362 | // Benchmarks revealed that this encoding was inefficient and that we could 363 | // reduce the total payload size of an index by 50% by using `repeated int32` 364 | // instead. The `repeated int32` encoding is admittedly more embarrassing to 365 | // work with in some programming languages but we hope the performance 366 | // improvements make up for it. 367 | repeated int32 range = 1; 368 | // (optional) The symbol that appears at this position. See 369 | // `SymbolInformation.symbol` for how to format symbols as strings. 370 | string symbol = 2; 371 | // (optional) Bitset containing `SymbolRole`s in this occurrence. 372 | // See `SymbolRole`'s documentation for how to read and write this field. 373 | int32 symbol_roles = 3; 374 | // (optional) CommonMark-formatted documentation for this specific range. If 375 | // empty, the `Symbol.documentation` field is used instead. One example 376 | // where this field might be useful is when the symbol represents a generic 377 | // function (with abstract type parameters such as `List`) and at this 378 | // occurrence we know the exact values (such as `List`). 379 | // 380 | // This field can also be used for dynamically or gradually typed languages, 381 | // which commonly allow for type-changing assignment. 382 | repeated string override_documentation = 4; 383 | // (optional) What syntax highlighting class should be used for this range? 384 | SyntaxKind syntax_kind = 5; 385 | // (optional) Diagnostics that have been reported for this specific range. 386 | repeated Diagnostic diagnostics = 6; 387 | } 388 | 389 | // Represents a diagnostic, such as a compiler error or warning, which should be 390 | // reported for a document. 391 | message Diagnostic { 392 | // Should this diagnostic be reported as an error, warning, info, or hint? 393 | Severity severity = 1; 394 | // (optional) Code of this diagnostic, which might appear in the user interface. 395 | string code = 2; 396 | // Message of this diagnostic. 397 | string message = 3; 398 | // (optional) Human-readable string describing the source of this diagnostic, e.g. 399 | // 'typescript' or 'super lint'. 400 | string source = 4; 401 | repeated DiagnosticTag tags = 5; 402 | } 403 | 404 | enum Severity { 405 | UnspecifiedSeverity = 0; 406 | Error = 1; 407 | Warning = 2; 408 | Information = 3; 409 | Hint = 4; 410 | } 411 | 412 | enum DiagnosticTag { 413 | UnspecifiedDiagnosticTag = 0; 414 | Unnecessary = 1; 415 | Deprecated = 2; 416 | } 417 | 418 | // Language standardises names of common programming languages that can be used 419 | // for the `Document.language` field. The primary purpose of this enum is to 420 | // prevent a situation where we have a single programming language ends up with 421 | // multiple string representations. For example, the C++ language uses the name 422 | // "CPlusPlus" in this enum and other names such as "cpp" are incompatible. 423 | // Feel free to send a pull-request to add missing programming languages. 424 | enum Language { 425 | UnspecifiedLanguage = 0; 426 | ABAP = 60; 427 | APL = 49; 428 | Ada = 39; 429 | Agda = 45; 430 | AsciiDoc = 86; 431 | Assembly = 58; 432 | Awk = 66; 433 | Bat = 68; 434 | BibTeX = 81; 435 | C = 34; 436 | COBOL = 59; 437 | CPP = 35; // C++ (the name "CPP" was chosen for consistency with LSP) 438 | CSS = 26; 439 | CSharp = 1; 440 | Clojure = 8; 441 | Coffeescript = 21; 442 | CommonLisp = 9; 443 | Coq = 47; 444 | Dart = 3; 445 | Delphi = 57; 446 | Diff = 88; 447 | Dockerfile = 80; 448 | Dyalog = 50; 449 | Elixir = 17; 450 | Erlang = 18; 451 | FSharp = 42; 452 | Fish = 65; 453 | Flow = 24; 454 | Fortran = 56; 455 | Git_Commit = 91; 456 | Git_Config = 89; 457 | Git_Rebase = 92; 458 | Go = 33; 459 | Groovy = 7; 460 | HTML = 30; 461 | Hack = 20; 462 | Handlebars = 90; 463 | Haskell = 44; 464 | Idris = 46; 465 | Ini = 72; 466 | J = 51; 467 | JSON = 75; 468 | Java = 6; 469 | JavaScript = 22; 470 | JavaScriptReact = 93; 471 | Jsonnet = 76; 472 | Julia = 55; 473 | Kotlin = 4; 474 | LaTeX = 83; 475 | Lean = 48; 476 | Less = 27; 477 | Lua = 12; 478 | Makefile = 79; 479 | Markdown = 84; 480 | Matlab = 52; 481 | Nix = 77; 482 | OCaml = 41; 483 | Objective_C = 36; 484 | Objective_CPP = 37; 485 | PHP = 19; 486 | PLSQL = 70; 487 | Perl = 13; 488 | PowerShell = 67; 489 | Prolog = 71; 490 | Python = 15; 491 | R = 54; 492 | Racket = 11; 493 | Raku = 14; 494 | Razor = 62; 495 | ReST = 85; 496 | Ruby = 16; 497 | Rust = 40; 498 | SAS = 61; 499 | SCSS = 29; 500 | SML = 43; 501 | SQL = 69; 502 | Sass = 28; 503 | Scala = 5; 504 | Scheme = 10; 505 | ShellScript = 64; // Bash 506 | Skylark = 78; 507 | Swift = 2; 508 | TOML = 73; 509 | TeX = 82; 510 | TypeScript = 23; 511 | TypeScriptReact = 94; 512 | VisualBasic = 63; 513 | Vue = 25; 514 | Wolfram = 53; 515 | XML = 31; 516 | XSL = 32; 517 | YAML = 74; 518 | Zig = 38; 519 | // NextLanguage = 95; 520 | // Steps add a new language: 521 | // 1. Copy-paste the "NextLanguage = N" line above 522 | // 2. Increment "NextLanguage = N" to "NextLanguage = N+1" 523 | // 3. Replace "NextLanguage = N" with the name of the new language. 524 | // 4. Move the new language to the correct line above using alphabetical order 525 | // 5. (optional) Add a brief comment behind the language if the name is not self-explanatory 526 | } -------------------------------------------------------------------------------- /test/scip/scip.zig: -------------------------------------------------------------------------------- 1 | //! Translated/derived from https://github.com/sourcegraph/scip/blob/main/scip.proto; 2 | //! See license at https://github.com/sourcegraph/scip/blob/main/LICENSE 3 | //! 4 | //! An index contains one or more pieces of information about a given piece of 5 | //! source code or software artifact. Complementary information can be merged 6 | //! together from multiple sources to provide a unified code intelligence 7 | //! experience. 8 | //! 9 | //! Programs producing a file of this format is an "indexer" and may operate 10 | //! somewhere on the spectrum between precision, such as indexes produced by 11 | //! compiler-backed indexers, and heurstics, such as indexes produced by local 12 | //! syntax-directed analysis for scope rules. 13 | 14 | const std = @import("std"); 15 | 16 | /// Index represents a complete SCIP index for a workspace this is rooted at a 17 | /// single directory. An Index message payload can have a large memory footprint 18 | /// and it's therefore recommended to emit and consume an Index payload one field 19 | /// value at a time. To permit streaming consumption of an Index payload, the 20 | /// `metadata` field must appear at the start of the stream and must only appear 21 | /// once in the stream. Other field values may appear in any order. 22 | pub const Index = struct { 23 | pub const tags = .{ 24 | .{ "metadata", 1 }, 25 | .{ "documents", 2 }, 26 | .{ "external_symbols", 3 }, 27 | }; 28 | 29 | /// Metadata about this index. 30 | metadata: Metadata, 31 | /// Documents that belong to this index. 32 | documents: std.ArrayListUnmanaged(Document), 33 | /// (optional) Symbols that are referenced from this index but are defined in 34 | /// an external package (a separate `Index` message). Leave this field empty 35 | /// if you assume the external package will get indexed separately. If the 36 | /// external package won't get indexed for some reason then you can use this 37 | /// field to provide hover documentation for those external symbols. 38 | external_symbols: std.ArrayListUnmanaged(SymbolInformation), 39 | }; 40 | 41 | pub const Metadata = struct { 42 | pub const tags = .{ 43 | .{ "version", 1 }, 44 | .{ "tool_info", 2 }, 45 | .{ "project_root", 3 }, 46 | .{ "text_document_encoding", 4 }, 47 | }; 48 | 49 | /// Which version of this protocol was used to generate this index? 50 | version: ProtocolVersion, 51 | /// Information about the tool that produced this index. 52 | tool_info: ToolInfo, 53 | /// URI-encoded absolute path to the root directory of this index. All 54 | /// documents in this index must appear in a subdirectory of this root 55 | /// directory. 56 | project_root: []const u8, 57 | /// Text encoding of the source files on disk that are referenced from 58 | /// `Document.relative_path`. 59 | text_document_encoding: TextEncoding, 60 | }; 61 | 62 | pub const ProtocolVersion = enum(u64) { 63 | unspecified_protocol_version = 0, 64 | }; 65 | 66 | pub const TextEncoding = enum(u64) { 67 | unspecified_text_encoding = 0, 68 | utf8 = 1, 69 | utf16 = 2, 70 | }; 71 | 72 | pub const ToolInfo = struct { 73 | pub const tags = .{ 74 | .{ "name", 1 }, 75 | .{ "version", 2 }, 76 | .{ "arguments", 3 }, 77 | }; 78 | 79 | /// Name of the indexer that produced this index. 80 | name: []const u8, 81 | /// Version of the indexer that produced this index. 82 | version: []const u8, 83 | /// Command-line arguments that were used to invoke this indexer. 84 | arguments: std.ArrayListUnmanaged([]const u8), 85 | }; 86 | 87 | /// Document defines the metadata about a source file on disk. 88 | pub const Document = struct { 89 | pub const tags = .{ 90 | .{ "language", 4 }, 91 | .{ "relative_path", 1 }, 92 | .{ "occurrences", 2 }, 93 | .{ "symbols", 3 }, 94 | }; 95 | 96 | /// The string ID for the programming language this file is written in. 97 | /// The `Language` enum contains the names of most common programming languages. 98 | /// This field is typed as a string to permit any programming langauge, including 99 | /// ones that are not specified by the `Language` enum. 100 | language: []const u8, 101 | /// (Required) Unique path to the text document. 102 | /// 103 | /// 1. The path must be relative to the directory supplied in the associated 104 | /// `Metadata.project_root`. 105 | /// 2. The path must not begin with a leading '/'. 106 | /// 3. The path must point to a regular file, not a symbolic link. 107 | /// 4. The path must use '/' as the separator, including on Windows. 108 | /// 5. The path must be canonical; it cannot include empty components ('//'), 109 | /// or '.' or '..'. 110 | relative_path: []const u8, 111 | /// Occurrences that appear in this file. 112 | occurrences: std.ArrayListUnmanaged(Occurrence), 113 | /// Symbols that are defined within this document. 114 | symbols: std.ArrayListUnmanaged(SymbolInformation), 115 | }; 116 | 117 | /// Symbol is similar to a URI, it identifies a class, method, or a local 118 | /// variable. `SymbolInformation` contains rich metadata about symbols such as 119 | /// the docstring. 120 | /// 121 | /// Symbol has a standardized string representation, which can be used 122 | /// interchangeably with `Symbol`. The syntax for Symbol is the following: 123 | /// ``` 124 | /// # ()+ stands for one or more repetitions of 125 | /// ::= ' ' ' ' ()+ | 'local ' 126 | /// ::= ' ' ' ' 127 | /// ::= any UTF-8, escape spaces with double space. 128 | /// ::= same as above, use the placeholder '.' to indicate an empty value 129 | /// ::= same as above 130 | /// ::= same as above 131 | /// ::= | | | | | | 132 | /// ::= '/' 133 | /// ::= '#' 134 | /// ::= '.' 135 | /// ::= ':' 136 | /// ::= '(' ').' 137 | /// ::= '[' ']' 138 | /// ::= '(' ')' 139 | /// ::= 140 | /// ::= 141 | /// ::= | 142 | /// ::= ()+ 143 | /// ::= '_' | '+' | '-' | '$' | ASCII letter or digit 144 | /// ::= '`' ()+ '`' 145 | /// ::= any UTF-8 character, escape backticks with double backtick. 146 | /// ``` 147 | /// 148 | /// The list of descriptors for a symbol should together form a fully 149 | /// qualified name for the symbol. That is, it should serve as a unique 150 | /// identifier across the package. Typically, it will include one descriptor 151 | /// for every node in the AST (along the ancestry path) between the root of 152 | /// the file and the node corresponding to the symbol. 153 | pub const Symbol = struct { 154 | pub const tags = .{ 155 | .{ "scheme", 1 }, 156 | .{ "package", 2 }, 157 | .{ "descriptors", 3 }, 158 | }; 159 | 160 | scheme: []const u8, 161 | package: Package, 162 | descriptors: std.ArrayListUnmanaged(Descriptor), 163 | }; 164 | 165 | /// Unit of packaging and distribution. 166 | /// 167 | /// NOTE: This corresponds to a module in Go and JVM languages. 168 | pub const Package = struct { 169 | pub const tags = .{ 170 | .{ "manager", 1 }, 171 | .{ "name", 2 }, 172 | .{ "version", 3 }, 173 | }; 174 | 175 | manager: []const u8, 176 | name: []const u8, 177 | version: []const u8, 178 | }; 179 | 180 | pub const Descriptor = struct { 181 | pub const Suffix = enum(u64) { 182 | unspecified_suffix = 0, 183 | /// Unit of code abstraction and/or namespacing. 184 | /// 185 | /// NOTE: This corresponds to a package in Go and JVM languages. 186 | namespace = 1, 187 | type = 2, 188 | term = 3, 189 | method = 4, 190 | type_parameter = 5, 191 | parameter = 6, 192 | macro = 9, 193 | // Can be used for any purpose. 194 | meta = 7, 195 | local = 8, 196 | }; 197 | 198 | pub const tags = .{ 199 | .{ "name", 1 }, 200 | .{ "disambiguator", 2 }, 201 | .{ "suffix", 3 }, 202 | }; 203 | 204 | name: []const u8, 205 | disambiguator: []const u8, 206 | suffix: Suffix, 207 | }; 208 | 209 | /// SymbolInformation defines metadata about a symbol, such as the symbol's 210 | /// docstring or what package it's defined it. 211 | pub const SymbolInformation = struct { 212 | pub const tags = .{ 213 | .{ "symbol", 1 }, 214 | .{ "documentation", 3 }, 215 | .{ "relationships", 4 }, 216 | }; 217 | 218 | /// Identifier of this symbol, which can be referenced from `Occurence.symbol`. 219 | /// The string must be formatted according to the grammar in `Symbol`. 220 | symbol: []const u8, 221 | /// (optional, but strongly recommended) The markdown-formatted documentation 222 | /// for this symbol. This field is repeated to allow different kinds of 223 | /// documentation. For example, it's nice to include both the signature of a 224 | /// method (parameters and return type) along with the accompanying docstring. 225 | documentation: std.ArrayListUnmanaged([]const u8), 226 | /// (optional) Relationships to other symbols (e.g., implements, type definition). 227 | relationships: std.ArrayListUnmanaged(Relationship), 228 | }; 229 | 230 | pub const Relationship = struct { 231 | pub const tags = .{ 232 | .{ "symbol", 1 }, 233 | .{ "is_reference", 2 }, 234 | .{ "is_implementation", 3 }, 235 | .{ "is_type_definition", 4 }, 236 | }; 237 | 238 | symbol: []const u8, 239 | /// When resolving "Find references", this field documents what other symbols 240 | /// should be included together with this symbol. For example, consider the 241 | /// following TypeScript code that defines two symbols `Animal#sound()` and 242 | /// `Dog#sound()`: 243 | /// ```ts 244 | /// interface Animal { 245 | /// ^^^^^^ definition Animal# 246 | /// sound(): string 247 | /// ^^^^^ definition Animal#sound() 248 | /// } 249 | /// class Dog implements Animal { 250 | /// ^^^ definition Dog#, implementation_symbols = Animal# 251 | /// public sound(): string { return "woof" } 252 | /// ^^^^^ definition Dog#sound(), references_symbols = Animal#sound(), implementation_symbols = Animal#sound() 253 | /// } 254 | /// const animal: Animal = new Dog() 255 | /// ^^^^^^ reference Animal# 256 | /// console.log(animal.sound()) 257 | /// ^^^^^ reference Animal#sound() 258 | /// ``` 259 | /// Doing "Find references" on the symbol `Animal#sound()` should return 260 | /// references to the `Dog#sound()` method as well. Vice-versa, doing "Find 261 | /// references" on the `Dog#sound()` method should include references to the 262 | /// `Animal#sound()` method as well. 263 | is_reference: bool, 264 | /// Similar to `references_symbols` but for "Go to implementation". 265 | /// It's common for the `implementation_symbols` and `references_symbols` fields 266 | /// have the same values but that's not always the case. 267 | /// In the TypeScript example above, observe that `implementation_symbols` has 268 | /// the value `"Animal#"` for the "Dog#" symbol while `references_symbols` is 269 | /// empty. When requesting "Find references" on the "Animal#" symbol we don't 270 | /// want to include references to "Dog#" even if "Go to implementation" on the 271 | /// "Animal#" symbol should navigate to the "Dog#" symbol. 272 | is_implementation: bool, 273 | /// Similar to `references_symbols` but for "Go to type definition". 274 | is_type_definition: bool, 275 | }; 276 | 277 | /// SymbolRole declares what "role" a symbol has in an occurrence. A role is 278 | /// encoded as a bitset where each bit represents a different role. For example, 279 | /// to determine if the `Import` role is set, test whether the second bit of the 280 | /// enum value is defined. In pseudocode, this can be implemented with the 281 | /// logic: `const isImportRole = (role.value & SymbolRole.Import.value) > 0`. 282 | pub const SymbolRole = enum(u64) { 283 | /// This case is not meant to be used; it only exists to avoid an error 284 | /// from the Protobuf code generator. 285 | unspecified_symbol_role = 0, 286 | /// Is the symbol defined here? If not, then this is a symbol reference. 287 | definition = 0x1, 288 | /// Is the symbol imported here? 289 | import = 0x2, 290 | /// Is the symbol written here? 291 | write_access = 0x4, 292 | /// Is the symbol read here? 293 | read_access = 0x8, 294 | /// Is the symbol in generated code? 295 | generated = 0x10, 296 | /// Is the symbol in test code? 297 | @"test" = 0x20, 298 | }; 299 | 300 | pub const SyntaxKind = enum(u64) { 301 | unspecified_syntax_kind = 0, 302 | 303 | /// Comment, including comment markers and text 304 | comment = 1, 305 | 306 | /// `;` `.` `,` 307 | punctuation_delimiter = 2, 308 | /// (), {}, [] when used syntactically 309 | punctuation_bracket = 3, 310 | 311 | /// `if`, `else`, `return`, `class`, etc. 312 | keyword = 4, 313 | 314 | /// `+`, `*`, etc. 315 | identifier_operator = 5, 316 | 317 | /// non-specific catch-all for any identifier not better described elsewhere 318 | identifier = 6, 319 | /// Identifiers builtin to the language: `min`, `print` in Python. 320 | identifier_builtin = 7, 321 | /// Identifiers representing `null`-like values: `None` in Python, `nil` in Go. 322 | identifier_null = 8, 323 | /// `xyz` in `const xyz = "hello"` 324 | identifier_constant = 9, 325 | /// `var X = "hello"` in Go 326 | identifier_mutable_global = 10, 327 | /// Parameter definition and references 328 | identifier_parameter = 11, 329 | /// Identifiers for variable definitions and references within a local scope 330 | identifier_local = 12, 331 | /// Identifiers that shadow other identifiers in an outer scope 332 | identifier_shadowed = 13, 333 | /// Identifier representing a unit of code abstraction and/or namespacing. 334 | /// 335 | /// NOTE: This corresponds to a package in Go and JVM languages, 336 | /// and a module in languages like Python and JavaScript. 337 | identifier_namespace = 14, 338 | 339 | /// Function references, including calls 340 | identifier_function = 15, 341 | /// Function definition only 342 | identifier_function_definition = 16, 343 | 344 | /// Macro references, including invocations 345 | identifier_macro = 17, 346 | /// Macro definition only 347 | identifier_macro_definition = 18, 348 | 349 | /// non-builtin types 350 | identifier_type = 19, 351 | /// builtin types only, such as `str` for Python or `int` in Go 352 | identifier_builtin_type = 20, 353 | 354 | /// Python decorators, c-like __attribute__ 355 | identifier_attribute = 21, 356 | 357 | /// `\b` 358 | regex_escape = 22, 359 | /// `*`, `+` 360 | regex_repeated = 23, 361 | /// `.` 362 | regex_wildcard = 24, 363 | /// `(`, `)`, `[`, `]` 364 | regex_delimiter = 25, 365 | /// `|`, `-` 366 | regex_join = 26, 367 | 368 | /// Literal strings: "Hello, world!" 369 | string_literal = 27, 370 | /// non-regex escapes: "\t", "\n" 371 | string_literal_escape = 28, 372 | /// datetimes within strings, special words within a string, `{}` in format strings 373 | string_literal_special = 29, 374 | /// "key" in { "key": "value" }, useful for example in JSON 375 | string_literal_key = 30, 376 | /// 'c' or similar, in languages that differentiate strings and characters 377 | character_literal = 31, 378 | /// Literal numbers, both floats and integers 379 | numeric_literal = 32, 380 | /// `true`, `false` 381 | boolean_literal = 33, 382 | 383 | /// Used for XML-like tags 384 | tag = 34, 385 | /// Attribute name in XML-like tags 386 | tag_attribute = 35, 387 | /// Delimiters for XML-like tags 388 | tag_delimiter = 36, 389 | }; 390 | 391 | /// Occurrence associates a source position with a symbol and/or highlighting 392 | /// information. 393 | /// 394 | /// If possible, indexers should try to bundle logically related information 395 | /// across occurrences into a single occurrence to reduce payload sizes. 396 | pub const Occurrence = struct { 397 | pub const tags = .{ 398 | .{ "range", 1 }, 399 | .{ "symbol", 2 }, 400 | .{ "symbol_roles", 3 }, 401 | .{ "override_documentation", 4 }, 402 | .{ "syntax_kind", 5 }, 403 | .{ "diagnostics", 6 }, 404 | }; 405 | 406 | /// Source position of this occurrence. Must be exactly three or four 407 | /// elements: 408 | /// 409 | /// - Four elements: `[startLine, startCharacter, endLine, endCharacter]` 410 | /// - Three elements: `[startLine, startCharacter, endCharacter]`. The end line 411 | /// is inferred to have the same value as the start line. 412 | /// 413 | /// Line numbers and characters are always 0-based. Make sure to increment the 414 | /// line/character values before displaying them in an editor-like UI because 415 | /// editors conventionally use 1-based numbers. 416 | /// 417 | /// Historical note: the original draft of this schema had a `Range` message 418 | /// type with `start` and `end` fields of type `Position`, mirroring LSP. 419 | /// Benchmarks revealed that this encoding was inefficient and that we could 420 | /// reduce the total payload size of an index by 50% by using `repeated int32` 421 | /// instead. The `repeated int32` encoding is admittedly more embarrassing to 422 | /// work with in some programming languages but we hope the performance 423 | /// improvements make up for it. 424 | range: [4]i32, 425 | /// (optional) The symbol that appears at this position. See 426 | /// `SymbolInformation.symbol` for how to format symbols as strings. 427 | symbol: []const u8, 428 | /// (optional) Bitset containing `SymbolRole`s in this occurrence. 429 | /// See `SymbolRole`'s documentation for how to read and write this field. 430 | symbol_roles: u32, 431 | /// (optional) CommonMark-formatted documentation for this specific range. If 432 | /// empty, the `Symbol.documentation` field is used instead. One example 433 | /// where this field might be useful is when the symbol represents a generic 434 | /// function (with abstract type parameters such as `List`) and at this 435 | /// occurrence we know the exact values (such as `List`). 436 | /// 437 | /// This field can also be used for dynamically or gradually typed languages, 438 | /// which commonly allow for type-changing assignment. 439 | override_documentation: std.ArrayListUnmanaged([]const u8), 440 | /// (optional) What syntax highlighting class should be used for this range? 441 | syntax_kind: SyntaxKind, 442 | /// (optional) Diagnostics that have been reported for this specific range. 443 | diagnostics: std.ArrayListUnmanaged(Diagnostic), 444 | }; 445 | 446 | /// Represents a diagnostic, such as a compiler error or warning, which should be 447 | /// reported for a document. 448 | pub const Diagnostic = struct { 449 | pub const tags = .{ 450 | .{ "severity", 1 }, 451 | .{ "code", 2 }, 452 | .{ "message", 3 }, 453 | .{ "source", 4 }, 454 | .{ "tags", 5 }, 455 | }; 456 | 457 | /// Should this diagnostic be reported as an error, warning, info, or hint? 458 | severity: Severity, 459 | /// (optional) Code of this diagnostic, which might appear in the user interface. 460 | code: []const u8, 461 | /// Message of this diagnostic. 462 | message: []const u8, 463 | /// (optional) Human-readable string describing the source of this diagnostic, e.g. 464 | /// 'typescript' or 'super lint'. 465 | source: []const u8, 466 | tags: std.ArrayListUnmanaged(DiagnosticTag), 467 | }; 468 | 469 | pub const Severity = enum(u64) { 470 | unspecified_severity = 0, 471 | @"error" = 1, 472 | warning = 2, 473 | information = 3, 474 | hint = 4, 475 | }; 476 | 477 | pub const DiagnosticTag = enum(u64) { 478 | unspecified_diagnostic_tag = 0, 479 | unnecessary = 1, 480 | deprecated = 2, 481 | }; 482 | 483 | /// Language standardises names of common programming languages that can be used 484 | /// for the `Document.language` field. The primary purpose of this enum is to 485 | /// prevent a situation where we have a single programming language ends up with 486 | /// multiple string representations. For example, the C++ language uses the name 487 | /// "CPlusPlus" in this enum and other names such as "cpp" are incompatible. 488 | /// Feel free to send a pull-request to add missing programming languages. 489 | pub const Language = enum(u64) { 490 | unspecified_language = 0, 491 | abap = 60, 492 | apl = 49, 493 | ada = 39, 494 | agda = 45, 495 | ascii_doc = 86, 496 | assembly = 58, 497 | awk = 66, 498 | bat = 68, 499 | bib_te_x = 81, 500 | c = 34, 501 | cobol = 59, 502 | cpp = 35, 503 | css = 26, 504 | c_sharp = 1, 505 | clojure = 8, 506 | coffeescript = 21, 507 | common_lisp = 9, 508 | coq = 47, 509 | dart = 3, 510 | delphi = 57, 511 | diff = 88, 512 | dockerfile = 80, 513 | dyalog = 50, 514 | elixir = 17, 515 | erlang = 18, 516 | f_sharp = 42, 517 | fish = 65, 518 | flow = 24, 519 | fortran = 56, 520 | git_commit = 91, 521 | git_config = 89, 522 | git_rebase = 92, 523 | go = 33, 524 | groovy = 7, 525 | html = 30, 526 | hack = 20, 527 | handlebars = 90, 528 | haskell = 44, 529 | idris = 46, 530 | ini = 72, 531 | j = 51, 532 | json = 75, 533 | java = 6, 534 | java_script = 22, 535 | java_script_react = 93, 536 | jsonnet = 76, 537 | julia = 55, 538 | kotlin = 4, 539 | la_te_x = 83, 540 | lean = 48, 541 | less = 27, 542 | lua = 12, 543 | makefile = 79, 544 | markdown = 84, 545 | matlab = 52, 546 | nix = 77, 547 | o_caml = 41, 548 | objective_c = 36, 549 | objective_cpp = 37, 550 | php = 19, 551 | plsql = 70, 552 | perl = 13, 553 | power_shell = 67, 554 | prolog = 71, 555 | python = 15, 556 | r = 54, 557 | racket = 11, 558 | raku = 14, 559 | razor = 62, 560 | re_st = 85, 561 | ruby = 16, 562 | rust = 40, 563 | sas = 61, 564 | scss = 29, 565 | sml = 43, 566 | sql = 69, 567 | sass = 28, 568 | scala = 5, 569 | scheme = 10, 570 | shell_script = 64, // Bash 571 | skylark = 78, 572 | swift = 2, 573 | toml = 73, 574 | te_x = 82, 575 | type_script = 23, 576 | type_script_react = 94, 577 | visual_basic = 63, 578 | vue = 25, 579 | wolfram = 53, 580 | xml = 31, 581 | xsl = 32, 582 | yaml = 74, 583 | zig = 38, 584 | // NextLanguage = 95; 585 | // Steps add a new language: 586 | // 1. Copy-paste the "NextLanguage = N" line above 587 | // 2. Increment "NextLanguage = N" to "NextLanguage = N+1" 588 | // 3. Replace "NextLanguage = N" with the name of the new language. 589 | // 4. Move the new language to the correct line above using alphabetical order 590 | // 5. (optional) Add a brief comment behind the language if the name is not self-explanatory 591 | }; 592 | --------------------------------------------------------------------------------