GSCode
A Visual Studio Code language extension that provides IntelliSense support for Call of Duty's scripting languages — GSC, CSC and GSH.
GSCode helps you to find and fix errors before the compiler has to tell you, streamlining scripting. Additionally, it adds rich IntelliSense into your editor to support the scripting process.
Black Ops III is the verified target. Call of Duty 4, World at War, Modern Warfare 2 and Black Ops are also supported, each with its own dialect: which keywords exist, whether imports merge (#include) or name a namespace (#using), and which engine data loads. The status bar shows which game is active.
Requirements
GSCode's language server requires the .NET 10 Runtime, available at Download .NET 10.0. You do not need the SDK.
Using GSCode
Open a folder containing your scripts in VS Code. GSCode activates automatically for .gsc, .csc, and .gsh files:
.gsc is a server-world script.
.csc is a client-world script.
.gsh is a shared header inserted into either world.
The extension defaults to the Black Ops III dialect (gscode.game: "bo3"). Run GSCode: Select
Game to switch — it lists the games the server actually supports and ticks the one running now —
or set cod4, waw, mw2, bo1, or bo3 in Settings. The active game is shown in the status
bar. Changing the game or raw/mod paths prompts you to reload the VS Code window; restarting only
the language server does not re-read those startup settings.
Game files, raw scripts, and mods
GSCode can analyze only the open workspace, or resolve it against the game's own scripts and mods.
These settings control that behavior:
| Setting |
Purpose |
gscode.raw.enabled |
Master switch for reading game raw files; defaults to true. |
gscode.rawPath |
Absolute path to the game's raw script folder. Leave empty for automatic discovery. |
gscode.modsPath |
Absolute path to the folder containing one subfolder per mod. |
gscode.rawFileWarningMode |
Warn when saving protected raw files: off, stock (default), or all. |
When a workspace is a mod or loose script folder, set gscode.rawPath so includes and path calls
resolve against the stock scripts. A mod overlays the raw folder: its copy wins for that mod, while
the raw copy is used as the fallback. Black Ops III normally uses share/raw; earlier supported
games use raw directly. GSCode does not modify stock files for you, and raw-file warnings are
there to help avoid editing the wrong copy.
Indexing and analysis
The default gscode.workspaceIndexingMode: "partial" indexes signatures for workspace-wide
navigation, references, and completion; cross-file diagnostics (unused imports, missing functions,
and the like) still only apply to files you have open. Use "full" to run those same cross-file
lints over every indexed file, so a script's Problems entry is complete even when it is closed —
this costs a background pass after indexing finishes, on top of the index itself. Use "off" when
only open files should be analyzed — note that some diagnostics need the index to answer at all
(an unresolved function call, an unused import, most missing-import checks) and simply
do not run with indexing off; a #using naming no file and a duplicate import are still reported,
since they need only the file and the disk. In "full" mode a closed file's problems stay complete
through everything that rewrites it: opening and closing it, a change on disk such as a branch
switch, and a workspace folder added mid-session. gscode.diagnostics.scope controls which indexed files publish
diagnostics at all: open, workspace (default, your workspace/mod files), or all (including
stock raw files) — full decides how COMPLETE a published file's diagnostics are, diagnostics.scope
decides WHICH files get any.
gscode.enableWorkspaceCache is enabled by default and stores analyzed scripts per workspace so
unchanged files can be restored quickly. If the cache becomes stale, run GSCode: Clear Cache and
Reindex from the Command Palette.
Cache location and manual reset
On Windows, the persistent cache is stored outside the repository at:
%APPDATA%\gscode\cache\<hash>.db
This normally expands to C:\Users\<you>\AppData\Roaming\gscode\cache. SQLite may also create
<hash>.db-wal and <hash>.db-shm beside the database. The filename is an opaque 16-character
hash of the workspace folders and resolved raw/mod roots, so it does not contain the project name.
The cache contains analyzed records only; deleting it cannot delete your scripts.
On macOS and Linux, use the equivalent OS-specific ApplicationData directory; the final
gscode/cache layout and hashed database naming are the same.
The safest reset is GSCode: Clear Cache and Reindex, which closes this workspace's database,
removes its database and SQLite sidecars, and reloads VS Code. If you need to remove it manually:
- Close VS Code windows using the workspace so the GSCode server releases the database.
- Open
%APPDATA%\gscode\cache in Explorer, or run explorer "$env:APPDATA\gscode\cache".
- Delete only this workspace's
<hash>.db, <hash>.db-wal, and <hash>.db-shm files.
- Reopen the workspace and wait for the cold index to finish.
Do not delete the entire cache directory unless you intentionally want to rebuild every workspace's
index. If you cannot identify the right hash, removing all *.db, *.db-wal, and *.db-shm files
in this directory after closing VS Code is safe for source files, but it resets all workspaces.
Cache records are restored only when the on-disk file content still matches. Changes to the server
build, bundled API data, or selected game invalidate old records automatically; no manual cleanup is
normally needed after an extension update. Set gscode.enableWorkspaceCache to false to disable
persistent caching entirely.
In-source pragmas
Pragmas are GSCode directives carried inside comments. They suppress GSCode output; they do not
change what the game's compiler or Linker accepts. disable and restore are C#'s pair, chosen
because each word says which way it goes — which on/off stops doing as soon as two are nested:
// #pragma disable 5014
foo_that_exists_only_in_a_custom_engine_build();
// #pragma restore 5014
Use the numeric diagnostic code shown in the Problems panel. Both 5014 and gscode-5014 are
accepted. A disable applies from its comment onward until the matching restore; an unmatched
disable continues to the end of the file. The directives can be in line, block, or documentation
comments.
Any code can be named, whatever severity it carries — errors, warnings, information and hints
alike, and syntax errors as readily as lints. If you know C#'s #pragma warning disable, note that
this is wider than it: there an error cannot be suppressed at all, so do not assume one survives a
disable here. Suppressing an error hides the report and nothing else — a file whose syntax errors
are turned off still does not parse, and the features that need a parsed file (completion,
go-to-definition, rename) stay degraded with nothing on screen explaining it. Suppress an error only
when you know why it is wrong.
The C# spelling #pragma warning disable 5014 is also accepted, so an early file written against it
keeps working. Prefer the short form: warning would suggest a narrowness this does not have.
To suppress every diagnostic in a region:
// #pragma disable all
legacy_or_generated_code();
// #pragma restore all
To leave a hand-formatted region untouched while keeping diagnostics enabled, use the separate
format target:
// #pragma disable format
hand_formatted_code();
// #pragma restore format
format affects GSCode formatting only; it does not suppress diagnostics. Prefer a specific code
over all so new diagnostics are not hidden accidentally — and note that all means all, so an
all region hides the errors in it too.
GSCode 1.5's // gscode ignore is still accepted as a legacy alias. It suppresses every diagnostic
on the one line below the comment, at any severity — no more, and it opens no region:
// gscode ignore
foo_that_exists_only_in_a_custom_engine_build();
// gsc ignore and the block form /* gscode ignore */ work the same way; a block comment covers
the line below the line it closes on. Prefer #pragma disable in new code — it names the
code it suppresses and says where it stops.
Editor feature settings
| Setting |
Default |
What it does |
gscode.codeLens.enabled |
false |
"N references" above each function and class declaration. |
gscode.inlayHints.parameterNames |
false |
Parameter-name hints before arguments at call sites. An argument that already spells the parameter's name — a local, or a macro named after it — is left unlabelled. |
gscode.inlayHints.inferredTypes |
false |
Inferred-type hints on local variable assignments. |
gscode.inlayHints.macroParameterNames |
false |
Parameter-name hints inside a #define invocation. Off because macro parameters are named for the macro's body, not the caller. |
gscode.completion.autoImport |
true |
Offer functions from scripts this file has not imported, adding the #using / #include when one is accepted. Waits for three characters, so names already in scope come first. |
gscode.completion.literals |
true |
Offer the workspace's known strings, localized strings and hash strings while typing inside a literal — notify and waittill names especially. |
gscode.completion.callPunctuation |
parensAndSemicolon |
What completing a function call inserts with it: off, parens, or parensAndSemicolon. |
gscode.completion.parameterHints |
true |
Show a function's parameters beside its name in the suggestion list. |
gscode.outline.showAssignments |
true |
Show variable and field assignments under their function in the outline. |
gscode.format.padParens |
true |
if ( x ) rather than if (x). |
gscode.format.padCallParens |
true |
foo( a, b ) rather than foo(a, b). |
gscode.format.padBrackets |
true |
a[ i ] rather than a[i]. |
gscode.format.spaceBeforeControlParen |
true |
if ( rather than if(; independent of padParens. |
gscode.format.maxBlankLines |
2 |
The longest run of blank lines the formatter keeps. |
gscode.format.sortDirectives |
true |
Group and sort the directive block at the top of a file. |
gscode.format.alignConsecutive |
true |
Line up the = of consecutive assignments. |
gscode.format.indentCaseLabels |
true |
case labels one level inside their switch. |
gscode.format.indentCaseBlocks |
false |
Indent a braced case body inside its label. Off puts its {, } and break; level with case. |
gscode.format.indentDevBlocks |
false |
Indent the body of a /# … #/ dev block. |
gscode.format.alignMaxPadding |
20 |
The most spaces alignment may add to one line. A far longer or shorter left-hand side keeps a single space instead of pushing its run across the screen. 0 removes the limit. |
gscode.format.fixCasing |
true |
Lowercase keywords (IsDefined → isdefined), and give functions, namespaces and classes the spelling they are declared with. Macros match exactly, so they are never touched. |
Commands and useful editor features
- GSCode: Show Server Output opens the language-server log.
- GSCode: Restart Language Server restarts a wedged server or picks up a rebuilt server binary.
- GSCode: Select Game picks the Call of Duty dialect this workspace targets, then reloads the
window to apply it. The list comes from the server, so it only ever offers games with an
implemented dialect, and the tick marks the game the server actually selected rather than what
the setting says — the two differ when a setting names something unrecognised.
- GSCode: Clear Cache and Reindex deletes this workspace's cache and reloads the window.
- GSCode: Open Documentation for Symbol opens the matching API page on gscode.net, for the game the server has selected — a Call of Duty 4 workspace opens Call of Duty 4's library, not Black Ops III's. It is also bound to
Shift+F1 in GSC, CSC, and GSH files.
- GSCode: Organize Imports removes every unused
#using/#include in the file and sorts the
directive block, in the same order Format Document uses (also
Shift+Alt+O and Source Action...).
- GSCode: Generate ScriptDoc Block writes the doc block for the function the cursor is in —
anywhere in it — with its parameters filled in, in the dialect's own ScriptDoc form.
Every command above is also in the editor's right-click menu, under GSCode, in GSC, CSC and GSH
files.
GSCode provides diagnostics, hover, completion, signature help, go-to-definition, references,
rename, document/workspace symbols, semantic tokens, folding, code lens, call/type hierarchy,
inlay hints, document links, formatting, and code actions. Formatting is whitespace-only and
refuses files with syntax errors; it verifies its output before applying edits.
For troubleshooting, set gscode.serverLogLevel to info or verbose and inspect the GSCode
Server output channel. gscode.trace.server can trace the LSP messages exchanged with VS Code.
Troubleshooting checklist
| Symptom |
What to check |
#using, #include, or path calls cannot be resolved |
Confirm gscode.game (or run GSCode: Select Game, which shows the game in force), gscode.raw.enabled, gscode.rawPath, and gscode.modsPath; reload the window after changing any of them. |
| Completions or references stop at the open file |
Make sure gscode.workspaceIndexingMode is not off, then wait for the status bar to finish indexing. |
| Diagnostics appear only in some files |
Check gscode.diagnostics.scope; open intentionally excludes closed files, while all includes stock raw scripts. |
| Results look stale after changing paths or game |
Use Developer: Reload Window. If the index is still wrong, run GSCode: Clear Cache and Reindex. |
| The server appears stuck or silent |
Open GSCode: Show Server Output, set gscode.serverLogLevel to info or verbose, and restart the language server. |
| The extension prompts for .NET |
Install the .NET 10 Runtime, not just a VS Code extension or the .NET SDK. |
For the formatter's exact whitespace rules and options, see the repository's formatting
guideline.
Release Notes
2.2.0 (latest)
Before you upgrade. The workspace cache rebuilds once on the first start, in a new binary format.
Format Document re-indents existing files once, to the new formatter rules below.
gscode.completion.fieldScope is removed, gscode.serverLogLevel now defaults to warning, and the
inlay hints (parameter names, inferred types) now default to off, like the code lens.
Faster, at any size.
- A Black Ops III workspace indexes in about 0.6 s, and opening the root of a game install no longer
walks its tool-output folders.
- At 50,000 files a warm start is about 4 seconds, and completion, a file's lint pass, CodeLens and
find-references cost what they cost at 1,000.
gscode.workspaceIndexingMode: "full" reports the cross-file problems of every indexed file, not
only open ones, and re-checks only the files an edit reaches.
Navigation.
- Go to Implementations (method overrides, a field's assignments) and Go to Type Definition (the
class a variable holds, the function a pointer names).
- Go to Definition, references and rename on fields and class members.
- Call hierarchy for class methods and for files that are not open.
- Hover links to the declaration.
Macros. Signature help and hover show what a macro expands to, __FILE__ and __FUNCTION__ hover
with their value, macro arguments can carry inlay hints, and the lints report what a macro expands
to at the place it is used.
Completion.
- Functions from scripts you have not imported, with the import written for you.
- The full list outside any function.
sys:: for the engine library.
- Function pointers without parentheses.
- Field and literal lists cut to what you have typed.
Formatting.
- New settings:
fixCasing gives keywords and calls their declared spelling; indentCaseLabels,
indentCaseBlocks and indentDevBlocks set the indentations the stock scripts split on; alignMaxPadding caps
alignment.
- Output now matches the stock scripts more closely: continuation lines, split conditions, blank
lines after blocks, spaced
?: and base-class colons, one case label per line.
- Formatting edits stay on the lines they change, so the caret stays where it was.
The right-click menu has a GSCode submenu: Organize Imports (which now also sorts the import
block), Generate ScriptDoc Block for the function under the cursor, Open Documentation for Symbol,
Select Game, and the server commands. The extension also runs in Restricted Mode now.
Fixes. Over fifty, among them: full indexing mode keeping every closed file's cross-file
problems through edits, branch switches and folder changes, diagnostics that went stale or
reappeared on closed files, .gsh
edits not reaching the files that insert them, a cache that missed files changed between sessions,
#define bodies the formatter could break, CRLF files formatted as one whole-file edit, and false
positives on shipped scripts.
The full list is in CHANGELOG.md.
2.0.2
xanim and anim are accepted as #precache asset types, and offered in completion.
gscode.format.spaceBeforeControlParen: turn off for if(, for(, while( instead of if (. Independent of padParens, so every combination of keyword space and interior padding is reachable.
2.0.1
- Format-on-type is no longer enabled by default. 2.0.0 shipped
editor.formatOnType on for GSC, CSC and GSH, so typing ; or } re-indented and re-aligned the surrounding lines. To opt back in, add "[gsc]": { "editor.formatOnType": true } (and likewise [csc], [gsh]) to your settings; Format Document and Format Selection are unaffected.
- Two more formatter spacing settings:
gscode.format.padCallParens (foo( a ) vs foo(a), separately from control-flow parens) and gscode.format.padBrackets (a[ i ] vs a[i]). Both default to the 2.0.0 behaviour.
- Fixed the formatter putting a space after a unary minus or address-of:
( -150, -1024, 304 ) came out ( - 150, - 1024, 304 ) and &funcname as & funcname.
2.0.0
A complete ground-up rewrite of the language server and extension for speed, low memory use, and accuracy.
- Rebuilt the entire pipeline from scratch: a span-based lexer, a provenance-tracking preprocessor (
#define/#insert/#if), a hand-written recursive-descent parser with error recovery, and symbol extraction — none of it ever throws, so a broken file still gets a full outline and diagnostics.
- Added first-class mod-tools support:
share/raw plus every mod under mods/ is indexed in isolation, mod folders overlay raw without crossing each other, and a workspace-only mode works with no game install at all.
- Centralised everything in one script database with structurally isolated GSC and CSC worlds and a shared GSH (header) store, backed by a persistent SQLite cache so cold starts restore unchanged files in seconds.
- Full modern LSP suite: live diagnostics, hover, completion, signature help, go-to-definition, go-to-implementation, go-to-type-definition, find-all-references (including string/hash/localized/anim literals), document highlight, semantic tokens, folding, selection ranges, document/workspace symbols, code lens, rename, call and type hierarchy, inlay hints, document links, formatting, and code actions.
- A hover names where its subject is declared and links to it, so a function or a macro reached through an
#insert says which file it came from without a jump. Go to Implementations answers the question go-to-definition cannot — which subclasses override this method — and Go to Type Definition on a local opens the class it holds.
- Completion offers functions from scripts the file has not imported yet, once three characters have been typed, and adds the
#using (or #include) line when one is accepted — qualified on Black Ops III, bare on the merge dialects, which is what each game actually needs. Turn it off with gscode.completion.autoImport.
- Type-flow inference powers inferred-type inlay hints and local-variable hovers, seeded with engine object-field types.
- Formatting (whole document, selection, and on-type) is whitespace-only and corruption-proof: it refuses files with syntax errors and re-checks its own output so it can never alter your tokens.
- Code actions cover remove-duplicate-
#using, add-missing-#using and Organize Imports. Generate ScriptDoc Block is a command — a doc block for the function under the cursor, with its parameters filled in and written in the dialect's own ScriptDoc form.
- Macros defined in
.gsh headers are first-class symbols with go-to-definition, references, and hover via token provenance.
- Added support for four earlier games — Call of Duty 4, World at War, Modern Warfare 2 and Black Ops — with each dialect's keywords, import style, function-pointer and ScriptDoc syntax, and bundled engine data driven by one game profile rather than by branching.
- Replaced
TA_TOOLS_PATH with gscode.rawPath and gscode.modsPath, both derived from the game install where possible, so a mod or a loose folder of scripts resolves against the game's own scripts.
- Added snippets for the common constructs. The dialect-specific ones (
foreach, function, class, new, #using, #precache, ScriptDoc) are served by the language server and are only offered where the selected game has the construct, rather than being offered everywhere with a note in the description. #precache is Black Ops III's alone, and its asset types are further split by world: a .gsc is offered only server-side types and a .csc gets the client_* family — a header (.gsh) sees both, since it is inserted into whichever world includes it.
- Expanded diagnostics: argument counts against builtin and script signatures, macro arity, unreachable code, unassigned and unused variables, duplicate imports, duplicate case labels, assignment used as a condition, inheritance cycles,
... placement, and a missing semicolon reported at the end of the statement that is missing it. Eight of 1.5's stricter checks also return, from duplicate macro definitions to reading the value of a thread call, each measured against all five games' shipped scripts before being given a severity.
- In-source suppression, carried inside comments:
#pragma disable|restore <code>|all|format silences a diagnostic per line, per code, or entirely — errors included, wider than the C# pragma the spelling comes from. 1.5's // gscode ignore still works as a legacy alias.
shift+F1 opens the API library page for the built-in function under the cursor, and commands cover showing the server log, restarting the server, and clearing the cache to reindex.
For releases before 2.0, and for the full 2.0 entry including what the rewrite removed, see the
changelog. New releases are written there; this section keeps the current one only.
Reporting Issues
GSCode is an independent implementation of a GSC parser, so it may not have exact parity with the
game's compiler. If the compiler (Linker) reports an error that GSCode does not — or GSCode reports
one on code that compiles — that is a bug worth filing.
The repository README has the full reporting
guide, including the details a report needs and a copy/paste template. The one requirement worth
repeating here: attach the smallest script that reproduces the problem. Reports without one will
not be investigated.
Known Issues
- A macro hover shows the macro's own body with the call site's arguments substituted. Macros used
inside that body are shown by name and are not themselves expanded, so one hover is one level
deep.
- A macro body longer than 240 characters is truncated with an ellipsis. A hover is a glance, not a
code listing; use go-to-definition to read the whole macro.
- Files that are not open report the problems found when they were indexed — syntax errors, unknown
directives and precache mistakes. The cross-file lints (unused
#using, private access, dev-block
calls, read-only writes) need an open document, so a file can gain problems on being opened.
Licence
GSCode is open-source software licenced under the GNU General Public License v3.0.
GSCode - Call of Duty GSC Language Extension
Copyright (C) 2026 Blakintosh
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Please see LICENSE.md for details.