diff --git a/runtime/ftplugin/just.vim b/runtime/ftplugin/just.vim
new file mode 100644
index 0000000000..6f2acddf96
--- /dev/null
+++ b/runtime/ftplugin/just.vim
@@ -0,0 +1,17 @@
+" Vim ftplugin file
+" Language: Justfile
+" Maintainer: Peter Benjamin <@pbnj>
+" Last Change: 2025 Jan 19
+" Credits: The original author, Noah Bogart
+
+" Only do this when not done yet for this buffer
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal iskeyword+=-
+setlocal comments=n:#
+setlocal commentstring=#\ %s
+
+let b:undo_ftplugin = "setlocal iskeyword< comments< commentstring<"
diff --git a/runtime/indent/just.vim b/runtime/indent/just.vim
new file mode 100644
index 0000000000..d7f82b118f
--- /dev/null
+++ b/runtime/indent/just.vim
@@ -0,0 +1,51 @@
+" Vim indent file
+" Language: Justfile
+" Maintainer: Peter Benjamin <@pbnj>
+" Last Change: 2025 Jan 19
+" Credits: The original author, Noah Bogart
+
+" Only load this indent file when no other was loaded yet.
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+setlocal indentexpr=GetJustfileIndent()
+setlocal indentkeys=0},0),!^F,o,O,0=''',0=\"\"\"
+
+let b:undo_indent = "setlocal indentexpr< indentkeys<"
+
+if exists("*GetJustfileIndent")
+ finish
+endif
+
+function GetJustfileIndent()
+ if v:lnum < 2
+ return 0
+ endif
+
+ let prev_line = getline(v:lnum - 1)
+ let last_indent = indent(v:lnum - 1)
+
+ if getline(v:lnum) =~ "\\v^\\s+%([})]|'''$|\"\"\"$)"
+ return last_indent - shiftwidth()
+ elseif prev_line =~ '\V#'
+ return last_indent
+ elseif prev_line =~ "\\v%([:{(]|^.*\\S.*%([^']'''|[^\"]\"\"\"))\\s*$"
+ return last_indent + shiftwidth()
+ elseif prev_line =~ '\\$'
+ if v:lnum == 2 || getline(v:lnum - 2) !~ '\\$'
+ if prev_line =~ '\v:\=@!'
+ return last_indent + shiftwidth() + shiftwidth()
+ else
+ return last_indent + shiftwidth()
+ endif
+ endif
+ elseif v:lnum > 2 && getline(v:lnum - 2) =~ '\\$'
+ return last_indent - shiftwidth()
+ elseif prev_line =~ '\v:\s*%(\h|\()' && prev_line !~ '\V:='
+ return last_indent + shiftwidth()
+ endif
+
+ return last_indent
+endfunction
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 37e6227090..a8f3d18bfa 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -661,6 +661,8 @@ local extension = {
jsp = 'jsp',
jl = 'julia',
just = 'just',
+ Just = 'just',
+ JUST = 'just',
kl = 'karel',
KL = 'karel',
kdl = 'kdl',
@@ -1650,8 +1652,11 @@ local filename = {
['.vsconfig'] = 'jsonc',
['bun.lock'] = 'jsonc',
['.justfile'] = 'just',
+ ['.Justfile'] = 'just',
+ ['.JUSTFILE'] = 'just',
['justfile'] = 'just',
['Justfile'] = 'just',
+ ['JUSTFILE'] = 'just',
Kconfig = 'kconfig',
['Kconfig.debug'] = 'kconfig',
['Config.in'] = 'kconfig',
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 31c88c80bd..855893530e 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -1877,6 +1877,7 @@ local patterns_hashbang = {
ruby = 'ruby',
['node\\(js\\)\\=\\>\\|js\\>'] = { 'javascript', { vim_regex = true } },
['rhino\\>'] = { 'javascript', { vim_regex = true } },
+ just = 'just',
-- BC calculator
['^bc\\>'] = { 'bc', { vim_regex = true } },
['sed\\>'] = { 'sed', { vim_regex = true } },
diff --git a/runtime/syntax/just.vim b/runtime/syntax/just.vim
new file mode 100644
index 0000000000..77423a3c91
--- /dev/null
+++ b/runtime/syntax/just.vim
@@ -0,0 +1,406 @@
+" Vim syntax file
+" Language: Justfile
+" Maintainer: Peter Benjamin <@pbnj>
+" Last Change: 2025 Jan 19
+" Credits: The original author, Noah Bogart
+
+if exists('b:current_syntax')
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+let b:current_syntax = 'just'
+
+" syncing fromstart prevents mismatched highlighting when jumping around in a justfile
+" linebreaks= keeps multi-line constructs highlighted correctly while typing
+syn sync fromstart linebreaks=10
+
+" a-zA-Z0-9_-
+syn iskeyword @,48-57,_,-
+
+syn match justComment "#.*$" contains=@Spell,justCommentTodo
+syn match justCommentInBody '#.*$' contained contains=justCommentTodo,justInterpolation,@justOtherCurlyBraces
+syn keyword justCommentTodo TODO FIXME XXX contained
+syn match justShebang "^\s*#!.*$" contains=justInterpolation,@justOtherCurlyBraces
+syn match justName "\h\k*" contained
+syn match justFunction "\h\k*" contained
+
+syn match justPreBodyComment "\v%(\s|\\\n)*%([^\\]\n)@3%(%(\s|\\\n)*\=)@!" contained
+
+syn region justRecipeParenDefault
+ \ matchgroup=justRecipeDepParamsParen start='\v%(\=%(\s|\\\n)*)@<=\(' end='\V)'
+ \ contained
+ \ contains=@justExpr
+syn match justRecipeSubsequentDeps '\V&&' contained
+
+syn match justRecipeNoDeps '\v:%(\s|\\\n)*\n|:#@=|:%(\s|\\\n)+#@='
+ \ transparent contained
+ \ contains=justRecipeColon
+ \ nextgroup=justPreBodyComment,@justBodies
+syn region justRecipeDeps start="\v:%(\s|\\\n)*%([a-zA-Z_(]|\&\&)" skip='\\\n' end="\v#@=|\\@1